Grants & scopes
A grant decides exactly which credential a project key can touch, what it can do, and for how long. Each grant = project × credential × a set of scopes, with a token TTL cap and a per-minute rate limit attached — changes take effect immediately.
The grant model
There is no "whole-vault access" in Tesska. Every grant is an explicit record: a chosen subset of one credential's scopes, granted to one project. There is exactly one grant per (project × credential) pair — configuring it again overwrites it.
- Project — the caller, holding a tsk_ scoped key issued in the console's Token Center
- Credential — one managed asset in the vault, addressed by platform + account
- Scopes — the subset of operations this project may perform, e.g. contents:read
- Guardrails — ttlMaxSec (token TTL cap) and rateLimitPerMin (per-minute rate limit)
Least privilege
Grant only the scopes you need. The broker enforces this server-side on every /v1/login call: effective scopes = granted scopes ∩ the key's own scope narrowing. Requesting any scope outside the grant fails the whole call with 403 — there is no silent downgrade.
POST /v1/login
Authorization: Bearer tsk_...
{ "platform": "github", "account": "deploy-bot",
"scope": ["contents:read", "issues:write"] }
# "issues:write" is not in the grant -> the whole call is denied
HTTP 403
{ "error": "operation not granted: issues:write" }TTL cap & per-minute rate limit
Every grant carries two guardrail fields, both enforced server-side:
- ttlMaxSec — maximum lifetime of the short-lived token, in seconds (default 900). Even if the upstream token lives longer, its expiry is capped to this value.
- rateLimitPerMin — maximum broker calls per minute for this (project × credential) pair (default 60). Exceeding it returns 429.
# More than rateLimitPerMin calls in 60s for this (project x credential)
HTTP 429
{ "error": "rate limit exceeded" }Project platform allowlist
Each project can carry a platform allowlist. An empty list means all platforms are allowed (the default); as soon as it contains at least one entry, the project may only call platforms on the list. The broker enforces the allowlist inside /v1/login, before the grant check — defense in depth layered on top of grants.
Introspect your grants
A caller can inspect itself at any time: GET /v1/grants returns every grant available to the current Bearer key — platform, account (alias), scopes and fulfillment (delivery mode).
curl https://tesska.com/v1/grants \
-H "Authorization: Bearer tsk_..."
HTTP 200
{
"project": "cm9x2k4f80001s6p1",
"grants": [
{
"platform": "github",
"account": "deploy-bot",
"scopes": ["contents:read", "pull_requests:write"],
"fulfillment": "auto"
},
{
"platform": "slack",
"account": "ops-bot",
"scopes": ["chat:write"],
"fulfillment": "auto"
}
]
}The response never contains secrets or tokens./v1/grants returns metadata only — to obtain a short-lived token, always go through /v1/login.
Changes and revocation take effect immediately
A grant is not a ticket issued once: every /v1/login reads the current grant live. Narrow the scopes, lower the TTL or rate limit, or revoke the grant in the console, and the very next call runs under the new rules — a revoked grant returns 403 not granted. Credentials can also be frozen with one click; frozen credentials disappear from /v1/grants as well.