@emilia-protocol/gate 0.9.4 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +190 -0
  3. package/README.md +75 -16
  4. package/action-control-manifest.js +26 -0
  5. package/adapters/_kit.js +47 -5
  6. package/adapters/aws.js +15 -4
  7. package/adapters/github-demo.mjs +25 -22
  8. package/adapters/github.js +1 -1
  9. package/adapters/jira.js +1 -0
  10. package/adapters/linear.js +1 -0
  11. package/adapters/salesforce.js +1 -0
  12. package/adapters/stripe.js +1 -1
  13. package/adapters/supabase.js +26 -4
  14. package/adapters/vercel.js +33 -4
  15. package/aec-execution.js +16 -4
  16. package/breakglass.js +285 -51
  17. package/control-plane.js +341 -0
  18. package/coverage.js +722 -0
  19. package/custody-demo.mjs +13 -3
  20. package/demo.mjs +9 -3
  21. package/deploy/helm/README.md +16 -8
  22. package/deploy/helm/emilia-gate/Chart.yaml +3 -1
  23. package/deploy/helm/emilia-gate/templates/NOTES.txt +3 -2
  24. package/deploy/helm/emilia-gate/templates/deployment.yaml +55 -1
  25. package/deploy/helm/emilia-gate/values.yaml +18 -2
  26. package/deploy/helm/emilia-gate-service/Chart.yaml +11 -0
  27. package/deploy/helm/emilia-gate-service/README.md +81 -0
  28. package/deploy/helm/emilia-gate-service/templates/NOTES.txt +12 -0
  29. package/deploy/helm/emilia-gate-service/templates/_helpers.tpl +83 -0
  30. package/deploy/helm/emilia-gate-service/templates/deployment.yaml +153 -0
  31. package/deploy/helm/emilia-gate-service/templates/migration-job.yaml +73 -0
  32. package/deploy/helm/emilia-gate-service/templates/networkpolicy.yaml +113 -0
  33. package/deploy/helm/emilia-gate-service/templates/pdb.yaml +14 -0
  34. package/deploy/helm/emilia-gate-service/templates/service.yaml +20 -0
  35. package/deploy/helm/emilia-gate-service/templates/serviceaccount.yaml +8 -0
  36. package/deploy/helm/emilia-gate-service/tests/fixtures/001_gate.sql +26 -0
  37. package/deploy/helm/emilia-gate-service/tests/fixtures/002_runtime_access.sql +32 -0
  38. package/deploy/helm/emilia-gate-service/tests/fixtures/gate.config.mjs +29 -0
  39. package/deploy/helm/emilia-gate-service/tests/render-check.sh +145 -0
  40. package/deploy/helm/emilia-gate-service/values.schema.json +145 -0
  41. package/deploy/helm/emilia-gate-service/values.yaml +166 -0
  42. package/deploy/sql/001-runtime.sql +707 -0
  43. package/deploy/terraform/README.md +24 -14
  44. package/deploy/terraform/main.tf +59 -0
  45. package/deploy/terraform/service/README.md +81 -0
  46. package/deploy/terraform/service/main.tf +718 -0
  47. package/deploy/terraform/service/outputs.tf +19 -0
  48. package/deploy/terraform/service/tests/validate.sh +24 -0
  49. package/deploy/terraform/service/tests/validation.tftest.hcl +168 -0
  50. package/deploy/terraform/service/variables.tf +459 -0
  51. package/deploy/terraform/service/versions.tf +10 -0
  52. package/deploy/terraform/variables.tf +95 -2
  53. package/deployment-attestation.js +248 -0
  54. package/eg1-conformance.js +46 -12
  55. package/enterprise.js +6 -2
  56. package/ep-assure.mjs +3 -2
  57. package/evidence-postgres.js +357 -0
  58. package/evidence.js +10 -3
  59. package/execution-binding.js +141 -26
  60. package/index.js +556 -115
  61. package/key-registry.js +51 -19
  62. package/mcp.js +4 -2
  63. package/network-witness.js +500 -0
  64. package/package.json +25 -5
  65. package/reliance-kernel.js +5 -6
  66. package/reliance-packet.js +43 -10
  67. package/reports/assurance-package.js +45 -19
  68. package/reports/reperform.js +78 -18
  69. package/reports/underwriter.js +1 -1
  70. package/settlement.js +300 -0
  71. package/store-postgres.js +14 -0
  72. package/store.js +26 -5
  73. package/strict-json.js +86 -0
  74. package/witness-postgres.js +97 -0
@@ -1,6 +1,6 @@
1
1
  <!-- SPDX-License-Identifier: Apache-2.0 -->
2
2
 
3
- # EMILIA Gate — reference Terraform module (`EP-GATE-TF-v1`)
3
+ # EMILIA Gate — deprecated Terraform module (`EP-GATE-TF-v1`)
4
4
 
5
5
  Terraform sibling of the Helm chart (`../helm`, `EP-GATE-HELM-v1`) for BYOC
6
6
  (bring-your-own-cloud) installs of the Trusted Action Firewall. Same container
@@ -13,6 +13,10 @@ in a cluster **you** control:
13
13
  | `kubernetes_deployment_v1.gate` | The gate pods — non-root, read-only rootfs, no privilege escalation, all capabilities dropped, no service-account token; pinned issuer keys arrive via a non-optional `secretKeyRef` to **your** Secret |
14
14
  | `kubernetes_service_v1.gate` | Cluster-internal Service (ClusterIP by default) in front of the pods |
15
15
 
16
+ **Deprecated:** this legacy module may run per-process in-memory consumption and
17
+ evidence state. New deployments should use `./service`, which targets
18
+ `apps/gate-service` and requires durable operator adapters.
19
+
16
20
  **Honest framing:** this is a *reference module* and it is *experimental*. It
17
21
  is a starting point for your platform team to review, fork, and own — not a
18
22
  managed service, not a turnkey production install. The deployer's cluster, the
@@ -57,8 +61,9 @@ module "emilia_gate" {
57
61
  namespace = "emilia" # must already exist
58
62
  image = "ghcr.io/your-org/emilia-gate@sha256:..." # pin a digest
59
63
 
60
- replicas = 2
64
+ replicas = 1
61
65
  issuer_keys_secret_name = "emilia-gate-issuer-keys" # NAME only, never keys
66
+ github_token_secret_name = "emilia-gate-github"
62
67
 
63
68
  # The deny-by-default policy the gate enforces (EP-ACTION-RISK-MANIFEST).
64
69
  manifest_json = file("${path.module}/manifest.json")
@@ -85,9 +90,11 @@ policy change rolls the pods.
85
90
  | `EP_GATE_MANIFEST_PATH` | ConfigMap mount | `/etc/emilia-gate/action-risk-manifest.json` |
86
91
  | `EP_GATE_METRICS_ENABLED` | `metrics_enabled` | `false` |
87
92
  | `EP_GATE_ISSUER_KEYS` | `secretKeyRef` → your existing Secret | — (required) |
93
+ | `GITHUB_TOKEN` | `github_token_secret_name` → existing Secret | — (optional) |
88
94
 
89
95
  Extra plain-text env goes in `extra_env` — **never secrets** (values in that
90
- map land in Terraform state); wire secret env from your own Secrets.
96
+ map land in Terraform state). Use `secret_env` for generic non-optional
97
+ `secretKeyRef` entries; the module accepts Secret names/keys, never values.
91
98
 
92
99
  ## Inputs
93
100
 
@@ -99,7 +106,10 @@ map land in Terraform state); wire secret env from your own Secrets.
99
106
  | `issuer_keys_secret_key` | `issuer-keys.json` | Key inside that Secret |
100
107
  | `name` | `emilia-gate` | DNS-1123 label |
101
108
  | `namespace` | `default` | Must already exist |
102
- | `replicas` | `2` | See replay-defense note below |
109
+ | `replicas` | `1` | Values above one require both shared backend references |
110
+ | `shared_consumption_backend`, `shared_evidence_backend` | `null` | Secret-backed adapter environment references required above one replica |
111
+ | `github_token_secret_name` | `null` | Existing GitHub token Secret reference |
112
+ | `secret_env` | `{}` | Generic environment-to-existing-Secret references |
103
113
  | `port` / `service_port` | `8080` / `8080` | |
104
114
  | `service_type` | `ClusterIP` | Anything more exposed is your explicit call |
105
115
  | `log_level` | `info` | `error` \| `warn` \| `info` \| `debug` |
@@ -116,12 +126,11 @@ Outputs: `service_name`, `namespace`, `service_endpoint`, `deployment_name`,
116
126
 
117
127
  ## Operational notes
118
128
 
119
- - **Replay defense across replicas:** with `replicas > 1`, one-time receipt
120
- consumption is only fleet-safe if the gate is configured with a *shared*
121
- durable consumption store (Redis/Postgres see
122
- `createDurableConsumptionStore` in `@emilia-protocol/gate`). The module
123
- deploys the pods either way; pointing them at a shared store (e.g. via
124
- `extra_env`) is your configuration responsibility.
129
+ - **Replay defense across replicas:** the module refuses `replicas > 1` unless
130
+ both `shared_consumption_backend` and `shared_evidence_backend` provide
131
+ complete Secret-backed environment references. That proves wiring exists,
132
+ not that a custom legacy image implements the required atomic adapters;
133
+ prefer the service module.
125
134
  - **Namespace:** not created by this module — pass one that exists.
126
135
  - **Ingress/TLS/NetworkPolicy/ServiceMonitor:** intentionally out of scope.
127
136
  The default posture is cluster-internal; exposure decisions belong to the
@@ -129,10 +138,11 @@ Outputs: `service_name`, `namespace`, `service_endpoint`, `deployment_name`,
129
138
 
130
139
  ## What has been validated
131
140
 
132
- `terraform fmt -check`, `terraform init -backend=false`, and
133
- `terraform validate` pass against Terraform 1.9.8 with
141
+ `packages/gate/deploy/terraform/tests/validate.sh` runs formatting, provider
142
+ schema validation, and mocked plan tests that cover the one-replica default,
143
+ multi-replica refusal, and Secret-backed environment rendering. It uses
144
+ Terraform 1.9.8 with
134
145
  `hashicorp/kubernetes` 2.38. A create-only `terraform plan` renders all three
135
- resources, and the plan-time input validations (unparseable manifest, inline
136
- key material, zero replicas) refuse as designed. The module has **not** been
146
+ resources, and the plan-time input validations refuse unsafe scaling. The module has **not** been
137
147
  applied against a live cluster as part of this repo's CI — treat it as
138
148
  reviewed reference code, not a certified install path.
@@ -35,6 +35,8 @@ locals {
35
35
  "app.kubernetes.io/part-of" = "emilia-protocol"
36
36
  "emiliaprotocol.ai/module-contract" = local.module_version
37
37
  "emiliaprotocol.ai/maturity" = "experimental"
38
+ "emiliaprotocol.ai/deprecated" = "true"
39
+ "emiliaprotocol.ai/replacement" = "emilia-gate-service"
38
40
  }, var.extra_labels)
39
41
 
40
42
  selector_labels = {
@@ -45,6 +47,34 @@ locals {
45
47
  # Same paths as the Helm chart's container contract.
46
48
  manifest_mount_dir = "/etc/emilia-gate"
47
49
  manifest_file = "action-risk-manifest.json"
50
+
51
+ secret_env_references = concat(
52
+ var.shared_consumption_backend == null ? [] : [var.shared_consumption_backend],
53
+ var.shared_evidence_backend == null ? [] : [var.shared_evidence_backend],
54
+ var.github_token_secret_name == null ? [] : [{
55
+ env_name = var.github_token_env_name
56
+ secret_name = var.github_token_secret_name
57
+ secret_key = var.github_token_secret_key
58
+ }],
59
+ [for env_name, reference in var.secret_env : {
60
+ env_name = env_name
61
+ secret_name = reference.secret_name
62
+ secret_key = reference.secret_key
63
+ }],
64
+ )
65
+ configurable_env_names = concat(
66
+ [for reference in local.secret_env_references : reference.env_name],
67
+ keys(var.extra_env),
68
+ )
69
+ reserved_env_names = toset([
70
+ "NODE_ENV",
71
+ "EP_GATE_PORT",
72
+ "EP_GATE_LOG_LEVEL",
73
+ "EP_GATE_EVIDENCE_STRICT",
74
+ "EP_GATE_MANIFEST_PATH",
75
+ "EP_GATE_METRICS_ENABLED",
76
+ "EP_GATE_ISSUER_KEYS",
77
+ ])
48
78
  }
49
79
 
50
80
  # The policy the gate enforces. Plain ConfigMap: the manifest is deny-by-default
@@ -156,6 +186,20 @@ resource "kubernetes_deployment_v1" "gate" {
156
186
  }
157
187
  }
158
188
 
189
+ dynamic "env" {
190
+ for_each = { for index, reference in local.secret_env_references : tostring(index) => reference }
191
+ content {
192
+ name = env.value.env_name
193
+ value_from {
194
+ secret_key_ref {
195
+ name = env.value.secret_name
196
+ key = env.value.secret_key
197
+ optional = false
198
+ }
199
+ }
200
+ }
201
+ }
202
+
159
203
  dynamic "env" {
160
204
  for_each = var.extra_env
161
205
  content {
@@ -233,6 +277,21 @@ resource "kubernetes_deployment_v1" "gate" {
233
277
  }
234
278
  }
235
279
  }
280
+
281
+ lifecycle {
282
+ precondition {
283
+ condition = var.replicas == 1 || (var.shared_consumption_backend != null && var.shared_evidence_backend != null)
284
+ error_message = "The deprecated legacy module refuses replicas > 1 without both shared_consumption_backend and shared_evidence_backend Secret references. Prefer terraform/service."
285
+ }
286
+ precondition {
287
+ condition = length(distinct(local.configurable_env_names)) == length(local.configurable_env_names)
288
+ error_message = "Secret-backed and literal environment variables must not use duplicate names."
289
+ }
290
+ precondition {
291
+ condition = length(setintersection(toset(local.configurable_env_names), local.reserved_env_names)) == 0
292
+ error_message = "secret_env and extra_env must not override module-managed environment variables."
293
+ }
294
+ }
236
295
  }
237
296
 
238
297
  resource "kubernetes_service_v1" "gate" {
@@ -0,0 +1,81 @@
1
+ # EMILIA Gate service Terraform module
2
+
3
+ This Kubernetes module deploys the BYOC image built from `Dockerfile.gate`. It
4
+ does not publish, default, or make a claim about an official EMILIA image.
5
+
6
+ The module creates a dedicated token-free ServiceAccount, migration Job,
7
+ two-replica Deployment, ClusterIP Service, PDB, and additive default-deny
8
+ NetworkPolicies. The migration Job is network-restricted, waits to complete,
9
+ and gates Deployment creation. Its immutable name includes the image, command,
10
+ and `migration_revision`; bump the revision when migration inputs change
11
+ without changing the image.
12
+
13
+ Only existing Secret names and keys enter Terraform. The operator config,
14
+ Postgres URL, Gate API token, and issuer roots are read at pod start with
15
+ non-optional references. Their values are not module variables and therefore
16
+ are not written to Terraform state by this module. The configuration Secret
17
+ must contain `gate.config.mjs` and `migrate.mjs`; these remain operator-owned
18
+ because `apps/gate-service` defines durable adapter contracts rather than
19
+ shipping an unsafe generic production fallback.
20
+
21
+ Set `migration_postgres_secret_name` and `migration_postgres_secret_key` to a
22
+ separate DDL-capable login Secret. Migrations enabled with a missing Secret or
23
+ the runtime Secret name are rejected; there is no fallback. The module cannot
24
+ compare Secret contents, so provision genuinely distinct credentials. The
25
+ long-lived service should use a least-privilege role that cannot alter schema.
26
+
27
+ ## Example
28
+
29
+ ```hcl
30
+ module "emilia_gate" {
31
+ source = "./packages/gate/deploy/terraform/service"
32
+
33
+ namespace = "emilia-gate"
34
+ image = "registry.example.com/security/emilia-gate-service@sha256:REPLACE"
35
+
36
+ configuration_secret_name = "emilia-gate-configuration"
37
+ postgres_secret_name = "emilia-gate-postgres"
38
+ migration_postgres_secret_name = "emilia-gate-postgres-migrate"
39
+ migration_postgres_secret_key = "database-url"
40
+ api_token_secret_name = "emilia-gate-api-token"
41
+ issuer_roots_secret_name = "emilia-gate-issuer-roots"
42
+ github_token_secret_name = "emilia-gate-github"
43
+
44
+ secret_env = {
45
+ SIEM_TOKEN = {
46
+ secret_name = "emilia-gate-siem"
47
+ secret_key = "token"
48
+ }
49
+ }
50
+
51
+ # For managed Postgres, disable the in-cluster selector and allow only the
52
+ # stable database endpoint or egress-proxy CIDR.
53
+ postgres_pod_labels = {}
54
+ postgres_egress_cidrs = ["10.40.12.8/32"]
55
+
56
+ # Standard NetworkPolicy cannot select FQDNs. Prefer stable egress proxies
57
+ # or an FQDN-aware CNI policy for KMS, GitHub, and SaaS SIEM endpoints.
58
+ kms_egress_cidrs = ["10.40.20.10/32"]
59
+ github_egress_cidrs = ["10.40.20.11/32"]
60
+ siem_egress_cidrs = ["10.40.20.12/32"]
61
+ }
62
+ ```
63
+
64
+ Configure the `kubernetes` provider in the calling root module. Create the
65
+ namespace and all Secrets outside this module. GitHub and generic secret-backed
66
+ environment inputs render only non-optional `secretKeyRef` metadata; Secret
67
+ values never become variables, Deployment literals, or module-managed state.
68
+ Prefer an immutable image digest
69
+ and a remote Terraform state backend with encryption and access logging even
70
+ though this module stores Secret references only.
71
+
72
+ ## Clusterless validation
73
+
74
+ ```bash
75
+ packages/gate/deploy/terraform/service/tests/validate.sh
76
+ ```
77
+
78
+ The script runs formatting checks, initializes the Kubernetes provider schema
79
+ in a temporary directory, runs `terraform validate`, and executes mocked plan
80
+ tests for migration credential separation and Secret-backed environment wiring.
81
+ It does not configure credentials or contact a Kubernetes cluster.