Data Access Boundary - noetl/ai-meta GitHub Wiki
Authoritative rule:
agents/rules/data-access-boundary.md
ยท Pairs with:
agents/rules/execution-model.md
NoETL platform data is accessible via the NoETL server API only. Workers โ including the system worker pool โ call the server's HTTP API for any read or write to NoETL-owned tables.
NoETL-owned tables: noetl.event, noetl.command,
noetl.execution, noetl.outbox, noetl.catalog,
noetl.credential, noetl.keychain, noetl.runtime, any
future noetl.*.
โโโโโโโโโโโโโโโโโโโโ HTTP โโโโโโโโโโโโโโโโโโโโโโโโ
โ Rust worker โโโโโโโโโโโโโโบโ NoETL server โ
โ (user pool) โ โ (Python today, โ
โโโโโโโโโโโโโโโโโโโโ โ Rust eventually) โ
โ โ
โโโโโโโโโโโโโโโโโโโโ HTTP โ โฒ โ
โ noetl-worker- โโโโโโโโโโโโโโบโ โ ONLY this talks โ
โ system-pool โ โ โ to Postgres โ
โโโโโโโโโโโโโโโโโโโโ โ โ โ
โโโโผโโโโโโโโโโโโโโโโโโโโ
โ
โ SQL
โผ
โโโโโโโโโโโโโโโโโโโโโโโโ
โ Postgres โ
โ noetl.event โ
โ noetl.command โ
โ noetl.outbox โ
โ noetl.catalog โ
โ noetl.credential โ
โ ... โ
โโโโโโโโโโโโโโโโโโโโโโโโ
Exception โ external subsystems
(auth, credential rotation, alerting)
โ
โ direct via tool kinds
โผ
โโโโโโโโโโโโโโโโโโโโโโโโ
โ External systems โ
โ (Auth0, Vault, โ
โ PagerDuty, ...) โ
โโโโโโโโโโโโโโโโโโโโโโโโ
Workers calling /api/internal/* carry the system pool's
service-account token; user-pool workers can't call those
routes (403). Everything else (/api/events, /api/catalog/*,
etc.) is open to all worker pools with appropriate auth.
The bearer-token plumbing reuses only existing NoETL primitives โ no new code:
+------------------------------------------+
| K8s Secret noetl-internal-api-token |
| key=token, value=<32-byte hex> |
+--------------------+---------------------+
|
| valueFrom.secretKeyRef
v
+------------------------------------------+
| Pod env (worker-system-pool) |
| NOETL_INTERNAL_API_TOKEN=<token> |
| NOETL_KEYCHAIN_ENV_VARS=NOETL_INTERNAL_API_TOKEN
+--------------------+---------------------+
|
| worker startup
| load_keychain_env_allowlist()
v
+------------------------------------------+
| ctx.secrets["NOETL_INTERNAL_API_TOKEN"] |
| = "<token>" |
+--------------------+---------------------+
|
| playbook command dispatch
v
+------------------------------------------+
| tool: http |
| auth: |
| type: bearer |
| credential: NOETL_INTERNAL_API_TOKEN|
| AuthResolver.resolve_bearer(): |
| ctx.get_secret("NOETL_INTERNAL_..") |
+--------------------+---------------------+
|
| HTTPS
v
+------------------------------------------+
| /api/internal/* + Authorization header |
| server: secrets.compare_digest |
| โ
200 OK |
+------------------------------------------+
Five hops, each an existing primitive: K8s Secret, env mount, NOETL_KEYCHAIN_ENV_VARS allow-list, AuthResolver, server's constant-time comparison gate.
Phase 2.a.3 turned out to be configuration-only โ no Rust code change. The deployment manifest adds two env vars; the playbook references the alias via the standard auth: block.
Three load-bearing reasons:
- Connection pool isolation โ workers scale 1โ50+ on backlog; direct DB access blows the platform connection pool and deadlocks the server's own API.
- Sharding readiness โ server may shard by execution_id; API boundary makes shard routing transparent to workers.
- Single point of consistency โ schema migrations, audit logging, RBAC, scrub all enforced at the server. Direct workers bypass them.
When a playbook's target is an external system (Auth0, Vault, GCS, PagerDuty, a tenant's Postgres), it uses tool kinds direct. That's just NoETL acting as a client to an external system โ the normal playbook pattern.
Examples of external-subsystem system playbooks (exempt from the rule):
-
system/authโ talks to Auth0 / Okta / SAML. -
system/credential_rotateโ talks to GCP Secret Manager / AWS Secrets Manager / Vault. -
system/notify_alertโ talks to PagerDuty / OpsGenie / Slack.
Examples of NoETL-state system playbooks (rule applies):
-
system/outbox_publisherโ readsnoetl.outboxโ HTTP to/api/internal/outbox/*. -
system/projectorโ writesnoetl.eventโ HTTP to/api/internal/events/project. -
system/scheduled_cleanupโ TTL onnoetl.*tables โ HTTP to/api/internal/cleanup/*.
The Python server's API surface doesn't expose endpoints for the operations the system pool's playbooks need. These get added in Phase 1 of #46.
Initial inventory:
| Endpoint | Replaces direct DB access by |
|---|---|
POST /api/internal/outbox/claim?limit=N |
Python claim_outbox_batch
|
POST /api/internal/outbox/mark-published |
Python mark_outbox_published
|
POST /api/internal/outbox/mark-failed |
Python mark_outbox_failed
|
GET /api/internal/outbox/pending-count |
(new) KEDA HTTP scaler source |
POST /api/internal/events/project |
Python projector batch INSERT |
Auth gate: service-account token only the system pool's K8s
ServiceAccount carries. User playbooks calling /api/internal/*
get 403.
-
Does it touch
noetl.*tables?- Yes โ API only.
- No โ tool kinds direct.
-
Does the API have the endpoints needed?
- Yes โ use them.
- No โ file sub-issue on
noetl/noetl/noetl/serverto add the endpoint before writing the playbook.
-
Auth? Always โ
/api/internal/*requires the system pool's service account token.
Codified 2026-06-02 (afternoon) per standing instruction. Full rationale + endpoint inventory in the authoritative rule above.