Authentication
Grounded now supports API-key auth on the SDK-backed /api/* routes without replacing dashboard session auth.
That means:
- the dashboard can continue using BetterAuth sessions
- scripts and services can use API keys
- the SDK does not need browser cookies or dashboard login state
Key Types
| Kind | Prefix | Intended Package | Notes |
|---|---|---|---|
secret | grd_sk_... | @grounded/client | can be tenant-scoped or agent-scoped |
publishable | grd_pk_... | @grounded/tracing | must be agent-scoped and trace-only |
Scopes
Grounded checks both the key kind and the key scopes.
Available scopes today:
agents:readagents:writeconnectors:readconnectors:writesessions:readinsights:readsnapshots:readsnapshots:writejobs:readjobs:writejob_loops:readjob_loops:writeevals:readevals:writetraces:readtraces:write
Route Families
| Route family | Required scope | Allowed kinds |
|---|---|---|
/api/agents | agents:read or agents:write | secret |
/api/connectors | connectors:read or connectors:write | secret |
/api/sessions | sessions:read | secret |
/api/insights | insights:read | secret |
/api/snapshots | snapshots:read or snapshots:write | secret |
/api/jobs | jobs:read or jobs:write | secret |
/api/job-loops | job_loops:read or job_loops:write | secret |
/api/evals | evals:read or evals:write | secret |
/api/traces | traces:read or traces:write | publishable, secret |
Publishable keys are intentionally narrow. They are for trace ingestion and trace readback, not for creating agents, snapshots, jobs, or connectors.
Scope Resolution
The API checks auth in this order:
- valid dashboard session
- valid bearer API key
If neither exists, the request is rejected.
If a key is present:
- its prefix identifies the record family
- the stored secret hash must match
- expired keys are rejected
- revoked keys are rejected
- route kind restrictions are enforced
- route scope restrictions are enforced
Tenant-Scoped vs Agent-Scoped
Secret keys can be:
- tenant-scoped
- agent-scoped
Agent-scoped keys are safer for service-to-service access because they can only operate on one agent.
Publishable keys must be agent-scoped.
Creating Keys
Key creation currently lives behind internal, session-authenticated routes:
GET /api/api-keysPOST /api/api-keysPOST /api/api-keys/:keyId/revoke
Secret Key Example
POST /api/api-keys
Content-Type: application/json
{
"kind": "secret",
"label": "ops worker",
"scopes": [
"connectors:read",
"connectors:write",
"sessions:read",
"snapshots:read",
"snapshots:write",
"jobs:read",
"jobs:write",
"evals:read"
]
}Publishable Key Example
POST /api/api-keys
Content-Type: application/json
{
"kind": "publishable",
"label": "support tracing",
"agentId": "agt_123",
"scopes": ["traces:write"]
}SDK Usage
@grounded/client
import { Grounded } from '@grounded/client';
const grounded = new Grounded({
apiKey: process.env.GROUNDED_SECRET_KEY!,
baseUrl: 'http://localhost:3001',
});@grounded/tracing
import { GroundedTracing } from '@grounded/tracing';
const tracing = new GroundedTracing({
publishableKey: process.env.GROUNDED_PUBLISHABLE_KEY!,
baseUrl: 'http://localhost:3001',
});Operational Notes
@grounded/clientsendsAuthorization: Bearer grd_sk_...@grounded/tracingsendsAuthorization: Bearer grd_pk_...- API-key access is intentionally limited to the SDK-backed route families above
- key management itself is not exposed through the SDK yet
Recommendations
- use agent-scoped keys for individual services whenever possible
- keep publishable keys trace-only
- rotate keys instead of reusing one broad tenant secret everywhere
- prefer separate keys for CI, backend APIs, and long-running workers