diff --git a/site/decodal-site/src/scripts/playground-examples.js b/site/decodal-site/src/scripts/playground-examples.js index 0c1cab9..84b6b42 100644 --- a/site/decodal-site/src/scripts/playground-examples.js +++ b/site/decodal-site/src/scripts/playground-examples.js @@ -1,173 +1,323 @@ export const playgroundExamples = [ { - id: 'service-deployment', - title: 'Service deployment', + id: 'product-api-production', + title: 'Production API deployment', activePath: 'main.dcdl', files: { 'main.dcdl': `let schema = import "./schemas/service.dcdl"; - prod = import "./env/prod.dcdl"; - shared = import "./shared/tags.dcdl"; + env = import "./env/production.dcdl"; + org = import "./org/platform.dcdl"; + release = import "./release.dcdl"; in - schema.Service & prod.ServicePatch & { - name = "api"; - image = "registry.example.com/api:2026-06-24"; - port = 9000 + 443; - public = prod.is_public && 9000 + 443 > 9000; - tags = shared.common ++ ["api", "edge"]; + schema.Service & env.Production & { + name = "checkout-api"; + image = release.image; + owner = org.teams.checkout; + port = 8443; + replicas = env.capacity.base_replicas + 2; + resources.cpu_milli = env.capacity.cpu_milli; + resources.memory_mb = env.capacity.memory_mb; + autoscaling.enabled = true; + autoscaling.min = env.capacity.base_replicas; + autoscaling.max = env.capacity.base_replicas * 4; + rollout.strategy = match release.risk { + "high": "canary"; + "medium": "rolling"; + _: "all-at-once"; + }; + rollout.canary_percent = match release.risk { + "high": 10; + _: 100; + }; + ingress.hosts = env.domains.public ++ ["checkout.internal.example.com"]; + labels = org.common_labels ++ ["service:checkout", "tier:edge"]; + alerts.pager = org.pagers.checkout; } `, 'schemas/service.dcdl': `Service = { name = String; image = String; - port = Int & > 443 default 8443; + owner = String; + port = Int & >= 1024 & <= 65535 default 8080; replicas = Int & > 0 default 2; - public = Bool default false; - feature.metrics = Bool default true; - feature.tracing = Bool default false; + resources.cpu_milli = Int & >= 100 default 500; + resources.memory_mb = Int & >= 128 default 512; + autoscaling.enabled = Bool default false; + autoscaling.min = Int & > 0 default 2; + autoscaling.max = Int & > 0 default 4; + rollout.strategy = String default "rolling"; + rollout.canary_percent = Int & >= 0 & <= 100 default 100; + observability.metrics = Bool default true; + observability.tracing = Bool default true; + observability.logs = Bool default true; + network.public = Bool default false; }; `, - 'env/prod.dcdl': `is_public = true; + 'env/production.dcdl': `capacity = { + base_replicas = 6; + cpu_milli = 2000; + memory_mb = 4096; +}; -ServicePatch = { - replicas = 3; - feature.tracing = true; +domains.public = ["checkout.example.com", "api.example.com"]; + +Production = { + network.public = true; + observability.tracing = true; + observability.logs = true; }; `, - 'shared/tags.dcdl': `common = ["decodal", "prod"]; + 'org/platform.dcdl': `teams = { + checkout = "team:checkout-platform"; + platform = "team:core-platform"; +}; + +pagers = { + checkout = "pagerduty:checkout-primary"; +}; + +common_labels = ["managed-by:decodal", "environment:production"]; +`, + 'release.dcdl': `version = "2026.06.15"; +risk = "high"; +image = "registry.example.com/checkout-api:2026.06.15"; `, }, }, { - id: 'access-policy', - title: 'Access policy', + id: 'tenant-enterprise-plan', + title: 'Enterprise tenant contract', activePath: 'main.dcdl', files: { 'main.dcdl': `let - policy = import "./policy/base.dcdl"; - teams = import "./policy/teams.dcdl"; - env = "prod"; + schema = import "./schemas/tenant.dcdl"; + plans = import "./plans.dcdl"; + region = import "./regions/eu.dcdl"; + customer = import "./customers/acme.dcdl"; in - { - service = "payments"; - readers = policy.defaultReaders ++ teams.observability; - writers = policy.defaultWriters ++ teams.payments; - admins = match env { - "prod": policy.breakglassAdmins; - _: teams.platform; + schema.Tenant & (plans.Enterprise // region.Europe // customer.Acme // { + slug = "acme"; + display_name = "Acme Manufacturing"; + limits.users = customer.seats + 25; + limits.projects = 200; + limits.storage_gb = 2048; + features.audit_log = true; + features.sso = true; + features.data_residency = true; + compliance.retention_days = match customer.contract_tier { + "regulated": 2555; + _: 365; }; - audit.required = env == "prod"; - audit.retention_days = match env { - "prod": 365; - _: 30; - }; - } + notifications.channels = customer.notification_channels ++ ["security"]; + }) `, - 'policy/base.dcdl': `defaultReaders = ["group:engineering", "group:support"]; -defaultWriters = ["group:platform"]; -breakglassAdmins = ["user:oncall-primary", "user:oncall-secondary"]; -`, - 'policy/teams.dcdl': `platform = ["group:platform-admins"]; -payments = ["group:payments-api", "group:payments-sre"]; -observability = ["group:observability"]; -`, - }, - }, - { - id: 'edge-worker', - title: 'Edge worker config', - activePath: 'main.dcdl', - files: { - 'main.dcdl': `let - schema = import "./schemas/worker.dcdl"; - routes = import "./routes.dcdl"; - production = true; -in - schema.Worker & { - name = "decodal-docs"; - compatibility_date = "2026-06-15"; - workers_dev = !production; - route.host = routes.primary_host; - route.paths = routes.docs_paths ++ routes.playground_paths; - cache.ttl_seconds = 60 * 60; - cache.bypass = !production; - } -`, - 'schemas/worker.dcdl': `Worker = { - name = String; - compatibility_date = String; - workers_dev = Bool default false; - route.host = String; - cache.ttl_seconds = Int & >= 0 default 300; - cache.bypass = Bool default false; + 'schemas/tenant.dcdl': `Tenant = { + slug = String; + display_name = String; + plan = String default "standard"; + limits.users = Int & > 0 default 25; + limits.projects = Int & > 0 default 20; + limits.storage_gb = Int & > 0 default 100; + features.sso = Bool default false; + features.audit_log = Bool default true; + features.data_residency = Bool default false; + compliance.region = String default "us"; + compliance.retention_days = Int & >= 30 default 90; + billing.currency = String default "USD"; + billing.invoice_day = Int & >= 1 & <= 28 default 1; }; `, - 'routes.dcdl': `primary_host = "decodal.example.com"; -docs_paths = ["/docs/*", "/assets/*"]; -playground_paths = ["/playground/*"]; + 'plans.dcdl': `Enterprise = { + plan = "enterprise"; + limits.users = 500; + limits.projects = 100; + limits.storage_gb = 1024; + features.sso = true; + features.audit_log = true; + billing.invoice_day = 15; +}; +`, + 'regions/eu.dcdl': `Europe = { + compliance.region = "eu-west"; + compliance.retention_days = 365; + billing.currency = "EUR"; +}; +`, + 'customers/acme.dcdl': `seats = 475; +contract_tier = "regulated"; +notification_channels = ["email", "slack", "pagerduty"]; + +Acme = { + billing.invoice_day = 10; +}; `, }, }, { - id: 'data-pipeline', - title: 'Data pipeline', + id: 'event-pipeline-prod', + title: 'Event ingestion pipeline', activePath: 'main.dcdl', files: { 'main.dcdl': `let schema = import "./schemas/pipeline.dcdl"; - presets = import "./presets/batch.dcdl"; - env = "staging"; + env = import "./env/prod.dcdl"; + sources = import "./sources/events.dcdl"; + sinks = import "./sinks/warehouse.dcdl"; + transforms = import "./transforms.dcdl"; in - schema.Pipeline & presets.Batch & { + schema.Pipeline & env.Production & { name = "events-rollup"; - source.topic = "events.raw"; - sink.table = "analytics.events_daily"; - workers = match env { - "prod": 8; - "staging": 3; - _: 1; + source.kafka_cluster = sources.cluster; + source.topic = sources.raw_topic; + source.consumer_group = "analytics-events-rollup"; + sink.table = sinks.daily_events_table; + sink.mode = "append"; + workers = env.scale.worker_count; + batch.size = env.scale.batch_size; + batch.timeout_seconds = 60 + 30; + schedule.cron = "*/15 * * * *"; + transforms = transforms.standard ++ transforms.privacy ++ ["aggregate_daily"]; + quality.max_lag_seconds = match env.tier { + "gold": 120; + "silver": 300; + _: 900; }; - alerts.enabled = env != "dev"; - transforms = presets.standardTransforms ++ ["dedupe", "aggregate_daily"]; + alerts.channels = env.alert_channels; } `, 'schemas/pipeline.dcdl': `Pipeline = { name = String; + source.kafka_cluster = String; source.topic = String; + source.consumer_group = String; sink.table = String; - workers = Int & > 0 default 1; + sink.mode = String default "append"; + workers = Int & > 0 default 2; batch.size = Int & >= 100 default 1000; batch.timeout_seconds = Int & > 0 default 60; - alerts.enabled = Bool default false; + schedule.cron = String; + quality.max_lag_seconds = Int & > 0 default 300; + dead_letter.enabled = Bool default true; + dead_letter.topic = String default "events.dead_letter"; }; `, - 'presets/batch.dcdl': `standardTransforms = ["parse_json", "validate_schema", "enrich_metadata"]; + 'env/prod.dcdl': `tier = "gold"; +alert_channels = ["slack:data-platform", "pagerduty:data-primary"]; -Batch = { - batch.size = 5000; - batch.timeout_seconds = 30 + 30; +scale = { + worker_count = 12; + batch_size = 5000; +}; + +Production = { + dead_letter.enabled = true; +}; +`, + 'sources/events.dcdl': `cluster = "kafka-prod-use1"; +raw_topic = "events.raw.v3"; +`, + 'sinks/warehouse.dcdl': `daily_events_table = "analytics.events_daily"; +`, + 'transforms.dcdl': `standard = ["parse_json", "validate_schema", "dedupe"]; +privacy = ["redact_ip", "hash_user_id"]; +`, + }, + }, + { + id: 'edge-cdn-policy', + title: 'Edge CDN routing policy', + activePath: 'main.dcdl', + files: { + 'main.dcdl': `let + schema = import "./schemas/edge.dcdl"; + domains = import "./domains.dcdl"; + security = import "./security.dcdl"; + env = "production"; +in + schema.EdgeApplication & security.StrictHeaders & { + name = "docs-and-playground"; + domains = domains.primary ++ domains.aliases; + origin.host = "origin-docs.internal.example.com"; + origin.port = 443; + tls.mode = "strict"; + cache.default_ttl_seconds = 60 * 60; + cache.static_ttl_seconds = 60 * 60 * 24; + cache.bypass_preview = env != "production"; + routing.docs_paths = ["/docs/*", "/assets/*"]; + routing.playground_paths = ["/playground/*"]; + waf.enabled = true; + waf.rules = security.managed_rules ++ ["block-country:kp", "rate-limit-login"]; + } +`, + 'schemas/edge.dcdl': `EdgeApplication = { + name = String; + origin.host = String; + origin.port = Int & >= 1 & <= 65535 default 443; + tls.mode = String default "strict"; + cache.default_ttl_seconds = Int & >= 0 default 300; + cache.static_ttl_seconds = Int & >= 0 default 86400; + cache.bypass_preview = Bool default false; + waf.enabled = Bool default true; +}; +`, + 'domains.dcdl': `primary = ["decodal.example.com"]; +aliases = ["www.decodal.example.com", "docs.decodal.example.com"]; +`, + 'security.dcdl': `managed_rules = ["owasp-core", "known-bots", "credential-stuffing"]; + +StrictHeaders = { + headers.hsts = "max-age=31536000; includeSubDomains; preload"; + headers.referrer_policy = "same-origin"; + headers.frame_options = "DENY"; }; `, }, }, { - id: 'diagnostics', - title: 'Diagnostics: invalid port', + id: 'incident-escalation', + title: 'Incident escalation policy', activePath: 'main.dcdl', files: { 'main.dcdl': `let - schema = import "./schemas/service.dcdl"; + schema = import "./schemas/oncall.dcdl"; + teams = import "./teams.dcdl"; + calendar = import "./calendar.dcdl"; + severity = "sev1"; in - schema.Service & { - name = "broken-api"; - port = 442; + schema.Policy & { + service = "payments"; + severity = severity; + primary = teams.payments_primary; + secondary = teams.platform_secondary; + notify = teams.payments_primary ++ teams.platform_secondary ++ calendar.executive_watch; + response_minutes = match severity { + "sev1": 5; + "sev2": 15; + _: 60; + }; + escalation.after_minutes = match severity { + "sev1": 10; + "sev2": 30; + _: 120; + }; + escalation.targets = teams.platform_secondary ++ calendar.executive_watch; + runbooks = ["runbooks/payments-api", "runbooks/database-failover"]; } `, - 'schemas/service.dcdl': `Service = { - name = String; - port = Int & > 443 default 8443; - replicas = Int & > 0 default 2; + 'schemas/oncall.dcdl': `Policy = { + service = String; + severity = String; + response_minutes = Int & > 0 default 30; + escalation.after_minutes = Int & > 0 default 60; + auto_page = Bool default true; + create_status_page = Bool default true; }; +`, + 'teams.dcdl': `payments_primary = ["user:pay-oncall-a", "user:pay-oncall-b"]; +platform_secondary = ["user:platform-sre-a", "user:platform-sre-b"]; +`, + 'calendar.dcdl': `executive_watch = ["user:vp-engineering", "user:cto"]; `, }, },