Skip to content

Declare and change data scopes

Grant, change, or revoke an app's data access by editing spec.dataScopes on its App CR. Enforcement follows at the data gateway within five minutes, with zero redeploys — the network perimeter does not change.

Preconditions

  • An onboarded app (Onboard a new app).
  • For a non-synthetic scope: the scope name must be mapped to a real target by platform configuration (deploy/data-gateway/sources-configmap.yaml, delivered via GitOps). Declaring a scope grants the app permission at the gateway; it does not create the source, and apps never hold data-source credentials — the gateway does.

Steps

1. Add a scope

Each scope is {name, sourceType, access} with sourceType one of synthetic | postgres | s3 and access one of read | readwrite:

kubectl patch app expense-helper -n tend-system --type merge -p '{
  "spec": {"dataScopes": [
    {"name": "synthetic", "sourceType": "synthetic", "access": "readwrite"},
    {"name": "orders-db", "sourceType": "postgres", "access": "read"}
  ]}}'

The patch replaces the whole list

A JSON merge patch replaces the dataScopes array. Always include every scope the app should keep, not just the one you are adding.

2. Wait for enforcement — no redeploy happens

The gateway evaluates the App CR's declared scopes per request, so the change is enforced within five minutes (in practice near-immediately). The app pods are not restarted and the perimeter objects are unchanged — the gateway decision moved, not the network:

kubectl get pods -n tend-app-expense-helper            # same pods, no restarts
kubectl get ciliumnetworkpolicies -n tend-app-expense-helper   # same three objects

Verify it worked

make gateway-request sends a request from inside the app's namespace with the app's own identity:

make gateway-request speaks only the synthetic route

MODE=read always issues GET /v1/sources/{name}/rows, which is the synthetic connector's route. A postgres scope is read at POST /v1/sources/{name}/query and an s3 scope at /objects, so pointing this target at orders-db returns 404 source not configured rather than 200 — the scope is fine, the route is wrong. The deny cases below are exact for every source type, because the scope check runs before the connector is looked up.

make gateway-request APP=expense-helper SOURCE=synthetic MODE=read
# [gateway-request] status: 200          — declared, allowed

make gateway-request APP=expense-helper SOURCE=payroll-db MODE=read
# [gateway-request] status: 403
# {"error":"undeclared-source"}          — not declared, denied

make gateway-request APP=expense-helper SOURCE=orders-db MODE=write
# [gateway-request] status: 403
# {"error":"insufficient-access"}        — declared read-only, write denied

Every decision (allow and deny) is logged. Check the per-app summary via the gateway's platform query API — it needs a Keycloak OIDC token with a platform role (app tokens are refused on this surface):

kubectl -n tend-system port-forward svc/data-gateway 18081:8080 &
curl -s -H "Authorization: Bearer $PLATFORM_TOKEN" \
  "http://localhost:18081/v1/logs/summary?app=tend-system/expense-helper" | jq
# {"allows": …, "denies": …, "lastDeny": {…}}

The same decisions appear in the app's evidence record under dataAccess (Query an app's evidence). Full endpoint semantics are in the data gateway API reference.

Revoke a scope

Patch the list without the scope you are revoking:

kubectl patch app expense-helper -n tend-system --type merge -p '{
  "spec": {"dataScopes": [
    {"name": "synthetic", "sourceType": "synthetic", "access": "readwrite"}
  ]}}'

Then watch the denial appear — again with no redeploy:

make gateway-request APP=expense-helper SOURCE=orders-db MODE=read
# [gateway-request] status: 403
# {"error":"undeclared-source"}

Troubleshooting

  • A declared request returns 503 {"error":"evaluator-error"} — the gateway fails closed when it cannot evaluate or cannot log; check the data-gateway deployment in tend-system.
  • A declared postgres/s3 scope is denied — confirm the scope name matches a target in the gateway's sources configuration; an unmapped name cannot be served.