Scopes control how services access other services' databases and APIs. Most cross-service API calls need no scope grant — you just declare the dependency under spec.dependencies and the builder injects the URL. Scopes are required only when the target service defines Bio-ID scope grants (e.g. relay:send, raterspot:rate); the grant is then created automatically (same-org auto-approves, cross-org needs approval).
| Access type | Grant system | What's shared | Declared in |
|---|---|---|---|
| Database access | Koko scope grants | Connection strings (MongoDB, Redis, Neo4j) | spec.scopes (owner) + CLI request (consumer) |
| API access | Bio-ID scope grants | Authenticated service-to-service API calls | spec.dependencies (consumer) |
No unauthenticated cross-service connections. Every dependency goes through an approval gate.
Services can share database access through Koko's scope system. The owner declares what it shares, a consumer requests access, and an org admin approves.
spec:
scopes:
- resource: mongodb
database: shared-data
allowedConsumers:
- service: my-consumer
access: readOnly
Only services listed in allowedConsumers can request access.
tawa scopes request --from owner-service --resource mongodb --access readOnly
The builder injects a connection string as an environment variable in the consumer service.
| Level | Description |
|---|---|
readWrite | Full CRUD on MongoDB, all Redis commands, full Neo4j access |
readOnly | MongoDB read preference enforced, Redis GET only, Neo4j read-only transactions |
The owner service name is uppercased with hyphens replaced by underscores:
SHARED_DATA_SVC_MONGODB_URI=mongodb://host:27017/shared-data
Your service's own MONGODB_URI is unaffected — scoped variables always include the owner service name as a prefix.
For service-to-service API calls, declare the target under spec.dependencies. transport defaults to janus and scopes are optional — add scopes only when the target defines Bio-ID scope grants:
spec:
dependencies:
- service: raterspot
scopes: [raterspot:rate] # optional — only when the target defines scopes
# transport omitted → defaults to janus (metered, JWT-verified via /i/)
A scoped dependency also provisions OAuth client-credentials (BIO_CLIENT_ID/BIO_CLIENT_SECRET) and opens a Bio-ID scope grant — same-org auto-approves, cross-org blocks the build until approved. See the catalog-info.yaml reference for the full dependency model.
| Transport | Injects {SERVICE}_URL as | Gas metered | Use for |
|---|---|---|---|
janus (default) | {JANUS_URL}/i/{service} | Yes | Almost everything — JWT-verified |
direct | raw K8s DNS http://{svc}.{svc}-{env}… | No | Same-org own-service UI/API splits only |
gateway | (no URL injected) | Yes | Calling the Janus public gateway directly |
transport defaults to janus — you rarely set it. direct only stays direct for same-org callers; a cross-org direct declaration is silently rerouted through Janus.
# Request database access from another service
tawa scopes request --from owner-service --resource mongodb --access readOnly
# List pending and approved scope requests
tawa scopes list
# Approve an incoming scope request (org admin)
tawa scopes approve <request-id>
# Deny an incoming scope request
tawa scopes deny <request-id>
# Revoke previously granted access
tawa scopes revoke <request-id>
# owner-service catalog-info.yaml
spec:
scopes:
- resource: mongodb
database: analytics
allowedConsumers:
- service: reporting-service
access: readOnly
# owner-service catalog-info.yaml
spec:
scopes:
- resource: redis
allowedConsumers:
- service: worker-service
access: readWrite
Last updated: June 24, 2026