Documentation
¶
Overview ¶
Package auth provides a lightweight, role-based access control layer for kojo's HTTP API. Its purpose is "spoiler prevention" — keeping an agent from incidentally reading other agents' Persona / configuration when it curls the API on its own. It is NOT a security boundary against a malicious agent: an agent runs as the same OS user as kojo itself and can read other agents' files directly. See README for the threat model.
Index ¶
- func AllowNonOwner(p Principal, method, path string) bool
- func AuthMiddleware(resolver *Resolver) func(http.Handler) http.Handler
- func EnforceMiddleware(next http.Handler) http.Handler
- func OwnerOnlyMiddleware(h http.Handler) http.Handler
- func WithPrincipal(ctx context.Context, p Principal) context.Context
- type Principal
- func (p Principal) CanDeleteOrReset(targetID string) bool
- func (p Principal) CanForkOrCreate() bool
- func (p Principal) CanMutateSelf(targetID string) bool
- func (p Principal) CanReadFull(targetID string) bool
- func (p Principal) CanSetPrivileged() bool
- func (p Principal) IsAgent() bool
- func (p Principal) IsOwner() bool
- type Resolver
- type Role
- type TokenStore
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AllowNonOwner ¶
AllowNonOwner gates non-Owner principals against a small whitelist of API routes. Unrecognised paths are denied with 403, and recognised paths may further be denied based on Principal/method/target.
This wrapper applies on the *agent-facing* (auth-required) listener only. The public listener uses OwnerOnlyMiddleware and never enters this gate.
The intent is "default deny". Routes are grouped into three buckets:
- Public reads — info, directory, agent list/get, avatar.
- Self-scoped reads/writes — files, messages, tasks, credentials, memory, slackbot config, notify sources, group memberships, avatar upload, persona / metadata patch, MCP, PreCompact hook. Permitted only when the path's {id} matches the principal.
- Privileged-cross-agent — delete / reset / checkin / unarchive / reset-session. Permitted for self by Agent, or for any target by PrivAgent.
Owner-only routes (sessions, git, files browser, oauth, embedding, push, custom-models, group DM mutate-as-owner, fork, /privilege, generate-*) fall through and 403. Note: POST /api/v1/groupdms (group creation) is exposed to Agent / PrivAgent below — the handler then enforces the caller-in-memberIds invariant.
func AuthMiddleware ¶
AuthMiddleware resolves Authorization: Bearer / X-Kojo-Token to a Principal and passes it through ctx. It does NOT enforce per-route policy — that is the handler's responsibility (or a separate gate).
func EnforceMiddleware ¶
EnforceMiddleware gates /api/v1/* requests by Principal/method/path using AllowNonOwner. It runs after AuthMiddleware (which set the Principal in ctx). Static files and non-API paths pass through.
func OwnerOnlyMiddleware ¶
OwnerOnlyMiddleware unconditionally tags every request as the Owner. Used on the public (Tailscale) listener that the kojo user accesses from their phone — the user's UX is preserved (no token required).
Types ¶
type Principal ¶
Principal identifies the actor behind a request.
func FromContext ¶
FromContext retrieves the Principal stashed in ctx by middleware. Defaults to RoleGuest when no principal is set.
func (Principal) CanDeleteOrReset ¶
CanDeleteOrReset returns true for delete/reset/unarchive/checkin/ reset-session ops. Owner: any. PrivAgent: any. Agent: self only.
func (Principal) CanForkOrCreate ¶
CanForkOrCreate returns true only for the Owner. Forking copies persona/memory and would leak the source agent's full state, so it is kept Owner-only even for privileged agents. Bare creation is also Owner-only.
func (Principal) CanMutateSelf ¶
CanMutateSelf returns true if the principal may issue self-scoped mutations (PATCH, reset, etc.) against targetID.
func (Principal) CanReadFull ¶
CanReadFull returns true if the principal can read the full record (Persona, Token-bearing fields, etc.) for the given target agent ID. Owners can read any. Agents can only read their own.
func (Principal) CanSetPrivileged ¶
CanSetPrivileged returns true only for the Owner. A privileged-agent must never be able to grant or revoke privilege.
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver maps a Bearer token to a Principal.
func NewResolver ¶
func NewResolver(tokens *TokenStore, isPrivileged func(string) bool) *Resolver
NewResolver builds a Resolver from a TokenStore and a privilege predicate (agent.Manager.IsPrivileged).
type Role ¶
type Role int
Role identifies the actor behind an HTTP request after middleware resolution.
const ( // RoleGuest is the default role for unauthenticated requests on the // auth-required listener. Guests can only read directory entries. RoleGuest Role = iota // RoleAgent is a regular agent-bound principal. It can fully read its // own data and is limited to DirectoryEntry on other agents. RoleAgent // RolePrivAgent extends RoleAgent with the ability to delete/reset // other agents (but not fork or read their full data). RolePrivAgent // RoleOwner is the kojo user. It has full access to everything. RoleOwner )
type TokenStore ¶
type TokenStore struct {
// contains filtered or unexported fields
}
TokenStore persists owner / agent tokens on disk (mode 0600) and keeps an in-memory index for O(1) reverse lookup.
Layout:
<base>/owner.token — owner secret (single line, hex) <base>/agent_tokens/<id> — per-agent secret (single line, hex)
The store does NOT depend on the agent package; agent.Manager is the caller that drives EnsureAgentToken / RemoveAgentToken on agent lifecycle events.
func NewTokenStore ¶
func NewTokenStore(base string, overrideOwner string) (*TokenStore, error)
NewTokenStore initializes a store rooted at base. It creates the directory tree if missing and loads any existing tokens. The owner token is created on first use if absent unless overrideOwner is non-empty (in which case overrideOwner is treated as the canonical owner token and is *not* persisted to disk).
func (*TokenStore) AgentToken ¶
func (s *TokenStore) AgentToken(agentID string) (string, error)
AgentToken returns the token for the given agent ID, generating and persisting one if it does not already exist.
func (*TokenStore) EnsureAgentToken ¶
func (s *TokenStore) EnsureAgentToken(agentID string) error
EnsureAgentToken is a convenience wrapper used at agent-create time.
func (*TokenStore) LookupAgent ¶
func (s *TokenStore) LookupAgent(token string) (string, bool)
LookupAgent returns the agent ID associated with the given token, if any.
func (*TokenStore) OwnerToken ¶
func (s *TokenStore) OwnerToken() string
OwnerToken returns the current owner token (hex, 64 chars).
func (*TokenStore) RemoveAgentToken ¶
func (s *TokenStore) RemoveAgentToken(agentID string)
RemoveAgentToken deletes the on-disk and in-memory token for an agent. Safe to call on an unknown ID. Invalid IDs are ignored.