Documentation
¶
Overview ¶
Package authtoken issues, stores, and resolves the operator bearer tokens that carry the Dejima team-auth model: three built-in roles (owner / operator / viewer) and an optional per-island scope. A token is presented on the operator API surface (the unix socket and the tailnet-pinned TCP listener) via an "Authorization: Bearer <token>" header — it *complements* the tailnet identity, it does not replace it: a request with no token is still the fully-trusted caller, while a request that carries one is attenuated to that token's role + island scope.
This is the exchange-down boundary for the layers *above* the runtime (Scusi, a Slack bot, any control plane): the human authenticates with full authority, then mints a strictly-narrower service token to hand to an automated caller — never its own credential (docs/security-boundary.md). The roles only ever *decrease* authority; there is no token that grants more than the trusted listener already does.
These tokens are distinct from the per-island tokens in internal/porttoken: porttoken authorizes an in-island *brain* on the host-internal autonomy listener (exchange-down into a container); authtoken authorizes an *operator* client on the control-plane listeners. The two stores never cross — a token from one is meaningless to the other's middleware.
At rest (~/.dejima/tokens.json, 0600) only token *metadata* and the SHA-256 of each secret are kept; the raw bearer is shown exactly once, at creation. A leaked store therefore can't be replayed.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Identity ¶
type Identity struct {
// TokenID is the issuing token's id, or "" for the trusted no-token caller.
TokenID string
// Subject is a short human label for the actor (the token label, its id, or
// "local" for the trusted caller) — what an audit record names.
Subject string
Role Role
// Owner is the tenant this identity acts as (multi-tenant ownership). A
// non-owner may only touch islands whose owner matches; the host owner
// (RoleOwner) bypasses via OwnsAll.
Owner string
// Islands is the token's island scope; empty means unrestricted (all islands).
Islands []string
}
Identity is the resolved who/role for an authenticated request. The API layer puts it on the request context so handlers — and Lane 1's audit log — can attribute and authorize. The zero Identity is meaningless; callers get one only from Resolve or by constructing the trusted-owner default explicitly.
func Resolve ¶
Resolve maps a presented bearer secret to its Identity. It hashes the input and compares against every stored hash in constant time (no early-out timing leak of which token, or how many, exist). ok=false for an empty or unknown token. A present-but-unknown token must be treated as an authentication failure by the caller — never as the trusted no-token caller.
func (Identity) MayTouch ¶
MayTouch reports whether this identity's island scope permits acting on the named island. An unscoped identity (the common case) may touch any island.
type Role ¶
type Role string
Role is one of the three built-in roles. Authority decreases owner → operator → viewer; the middleware (internal/api) maps each route to the minimum role that may reach it.
const ( // RoleOwner is full authority — the same surface a trusted (no-token) caller // has, including destructive ops (purge), credential management, and token // administration. Mint sparingly. RoleOwner Role = "owner" // RoleOperator may drive island lifecycle (create, hibernate/wake, reset, // upgrade, clone, exec, attach, Port/capability grants) but NOT purge an // island, manage credentials/tokens, or touch daemon administration. RoleOperator Role = "operator" // RoleViewer is read + observe only: list/status/overview/logs/events/audit. // No mutation, no interactive attach. RoleViewer Role = "viewer" )
type Token ¶
type Token struct {
ID string `json:"id"` // short public handle; revoke by this
Label string `json:"label,omitempty"` // human note ("scusi-prod", "phone")
Role Role `json:"role"` // owner | operator | viewer
// Owner is the tenant this token acts as (multi-tenant ownership). An
// operator/viewer token scoped to owner "amanda" may only see/act on islands
// amanda owns; a RoleOwner token is the host admin and sees all regardless.
// Set at mint (defaults to the host owner); server-authoritative, never
// client-supplied on a request.
Owner string `json:"owner,omitempty"`
Islands []string `json:"islands,omitempty"` // static scope; empty = every island (still bounded by Owner)
Hash string `json:"hash"` // sha-256 hex of the secret
CreatedAt time.Time `json:"created_at"`
}
Token is the stored metadata for one issued bearer token. The secret itself is never persisted — only Hash (its SHA-256) — so the store can list and revoke tokens without ever holding a replayable credential.
func Create ¶
func Create(label string, role Role, islands []string, owner string) (secret string, tok Token, err error)
Create mints a fresh token: a 256-bit random secret returned to the caller exactly once, plus persisted metadata (role, scope, the secret's hash). The raw secret is never stored. islands scopes the token to those island names (empty = unrestricted). Returns the secret and the stored Token.