07 — API, data and configuration contracts
07 — API, data and configuration contracts#
Gateway API surface#
Use a versioned /v1/gateways namespace. Management routes require customer RBAC; node routes require a node identity. Binding to tenant/deployment comes from authentication, not a trusted body parameter.
| Method and path | Caller | Semantics |
|---|---|---|
POST /v1/gateways/enrollment-grants |
Deployment admin | Create expiring scoped one-use grant |
POST /v1/gateways/enroll |
Bootstrap grant + node proof | Atomically consume grant; create node identity |
POST /v1/gateways/{id}/heartbeat |
Same node | Report sequence, versions, capacity and health |
GET /v1/gateways/{id}/assignment |
Same node | Signed configuration/policy assignment; ETag supported |
GET /v1/policy-bundles/{digest} |
Authorized node/admin | Immutable signed bundle |
POST /v1/gateways/{id}/events |
Same node | Bounded batch; durable idempotent ingestion |
POST /v1/gateways/{id}/credentials/rotate |
Same node + valid proof | Rotate scoped credential with bounded overlap |
POST /v1/gateways/{id}/revoke |
Deployment admin | Revoke node and record operator reason |
POST /v1/policies/{id}/simulate |
Policy editor | Evaluate synthetic fixtures; no provider forwarding |
POST /v1/policies/{id}/publish |
Security admin | Validate, sign and create immutable version |
Policy publication uses optimistic concurrency (If-Match or equivalent revision) so one editor cannot overwrite another silently. Use idempotency keys for administrative mutations with retriable clients. Store key, caller scope, operation and request hash; reject reuse with a different payload.
Responses include a request ID. Standard errors contain machine-readable code, safe message, retryable and field errors. Use 401 for unauthenticated, 403 for unauthorized, 409 for conflict, 413 for size limit, 422 for invalid configuration, 429 for capacity and 503 for required dependency unavailable. Never return raw model output or credentials in errors.
Event contract#
Illustrative metadata-only event:
{
"schema_version": "1",
"event_id": "evt_example_unique_id",
"gateway_id": "gw_example",
"sequence": 1042,
"observed_at": "2026-09-19T10:00:00Z",
"principal_ref": "principal_example",
"team_ref": "team_finance",
"destination_id": "approved_ai_service",
"protocol_adapter": "example-api-v1",
"policy_version": "policy_42",
"engine_version": "engine_example",
"evaluated_action": "MASK",
"enforced_action": "MASK",
"coverage": "inspected",
"findings": [{"category": "BANK_ACCOUNT", "count": 1}],
"reason_code": "TEAM_RESTRICTED_DATA",
"inspection_ms": 24
}The example latency is illustrative, not a measured claim. Server attaches authenticated deployment/tenant and ingestion time. Validate bounded strings, enum values, counts and timestamps. Do not include prompt, replacement value, credential headers or full content-bearing URL.
Batch response returns accepted/duplicate/rejected event IDs and permanent error codes. Acknowledgement means committed durable storage, not receipt in memory. Gateway removes only acknowledged records. Retry transient failure with bounded exponential backoff/jitter. Partial failures must not cause already accepted events to be duplicated in downstream reports.
Data model#
Proposed entities extend the existing organization/team model:
| Entity | Key relationships and constraints |
|---|---|
| Deployment | Immutable deployment ID, mode, schema/config version |
| Gateway | Deployment/tenant, unique node identity, lifecycle status, last heartbeat |
| Gateway credential | Node, key fingerprint/hash, validity, revoked time |
| Enrollment grant | Hashed grant, scope, expiry, usage count with atomic consumption |
| Policy version | Tenant/policy/version, content digest, signature, capability requirements |
| Assignment | Gateway/cohort, bundle digest, monotonically increasing generation |
| Decision event | Tenant/gateway/event ID unique; observed and ingested timestamps |
| Outbox job | Event/destination unique, state, attempts, next attempt, lease owner/expiry |
| Integration | Tenant, approved endpoint, encrypted secret reference, enabled state |
| Admin audit | Actor, operation, target, before/after digest, time, reason |
| License state | Provider, entitlement references, last verification, allowed scope |
Index gateway health by deployment/status/time; events by tenant/time and principal/time; jobs by state/next-attempt. Partition event tables only when measured volume and deletion behavior justify it. Scope every query by authenticated tenant; optional row-level security adds defense but does not replace application authorization.
Worker transaction semantics#
Within a short transaction claim eligible jobs using row locks/leases, then commit the claim. Perform remote delivery outside the database lock. On completion, update only if the lease is still owned. Expired leases are reclaimable. A crash after remote acceptance but before local completion can duplicate delivery: provide event IDs and document at-least-once behavior rather than claiming exactly-once SIEM delivery.
Permanent failures enter a visible dead-letter state. Operators can inspect safe metadata and retry after fixing the destination. Retention cleanup must not delete data needed by pending jobs without a deliberate policy and alert.
Proposed configuration#
This example describes a private gateway role. The parser and secret providers must be implemented before use.
schema_version: 1
deployment:
id: customer-assigned-deployment-id
profile: self_hosted
role: gateway
network:
proxy_bind: "0.0.0.0:8443"
proxy_transport: tls
client_cidrs: ["10.20.0.0/16"]
management_bind: "127.0.0.1:9090"
identity:
mode: trusted_upstream
trusted_upstream_ca_ref: "file:/etc/zotline/trust/upstream-ca.pem"
unknown_principal_action: restrictive_policy
control:
url: "https://zotline-admin.customer.example"
node_credential_ref: "file:/etc/zotline/secrets/node-credential"
policy:
cache_path: "/var/lib/zotline/policy"
verification_keys_path: "/etc/zotline/trust/policy-keys.json"
invalid_bundle_action: block
inspection:
mode: deterministic_only
unsupported_action: block
max_decompressed_body_bytes: 10485760
max_inflight_requests: 100
audit:
mode: metadata
spool_path: "/var/lib/zotline/spool"
max_spool_bytes: 10737418240
full_action: block
updates:
mode: customer_approved
telemetry:
vendor_export: falseThe capacities are sample values needing workload validation. The trusted-upstream example requires an upstream that actually provides authenticated identity; it is not a default for arbitrary laptops. No inference endpoint means policies requiring semantic inspection cannot activate. Separate TLS proxy/inspection certificate references and complete destination ACLs are required before this partial example could form a valid deployment configuration.
Configuration validation and lifecycle#
Reject unknown keys and invalid combinations. Require explicit production authentication, durable storage, trust material and valid policy before readiness. Reject vendor endpoints in strict private mode unless explicitly approved by a customer egress policy. Secret providers must fail safely without printing secret values.
Configuration precedence: packaged defaults → customer configuration → explicitly documented operator overrides. Do not permit arbitrary environment variables to override trust or tenant identity invisibly. Hash effective non-secret configuration and expose its version in health.
Hot reload may cover safe limits, destination catalog and verified policy assignments. Listener addresses, trust hierarchy, database schema and identity authority generally require a staged restart/migration. Validate new configuration before atomically activating it; retain last known good configuration and log the actor/change.
Health contracts#
/livez means process/event loop alive. /readyz means this role can serve its intended workload; gateway readiness includes valid policy, certificates, usable spool and required engine. /status is authenticated and returns component states and safe version information. Readiness checks must not create billable model requests continuously or expose sensitive configuration publicly.
Compatibility#
Every release manifest records application, gateway API major, event schema, policy schema, database migration bounds, engine and model profile compatibility. New optional event fields are additive; breaking changes require a new major schema. Control plane should support an explicitly tested previous gateway version during rolling upgrades. The window is a release decision, not “all old versions forever.”