identity

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 27, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package identity answers one question: who owns this request?

SEAM 2 of 4. The engine deliberately does not own authentication. It defines the question and consumes the answer; how the answer is produced is injected.

The reason is the open-core boundary. The engine now hosts several teams and ships a session-backed provider of its own, so it is no longer true that authentication lives entirely outside it. What is still true is the part that matters: a wrapping application resolves an owner from a signed token, an org membership, or its own sessions, and must be able to do so without fighting the engine — usually by forking, which defeats the point of a module.

The seam survives because account.Sessions is an implementation of Provider rather than a replacement for it. The engine's own sign-in goes through the same interface a wrapping application swaps out, which is why adding teams, people and sessions changed no handler, no store query and no orchestrator call.

Everything downstream — handlers, storage, the orchestrator — takes an OwnerID and never asks where it came from.

Index

Constants

View Source
const DefaultOwnerID = "owner-local"

DefaultOwnerID is the owner every resource belongs to in a single-owner deployment.

It is a fixed value rather than a generated one so that the schema's owner_id column is populated identically across restarts and reinstalls, and so a later migration to multi-owner has one known value to rewrite.

Variables

View Source
var ErrUnauthenticated = errors.New("identity: unauthenticated")

ErrUnauthenticated means no owner could be resolved for a request.

Functions

func Middleware

func Middleware(p Provider) func(http.Handler) http.Handler

Middleware resolves the owner once per request and puts it in the context.

Doing this in one place, rather than in each handler, is deliberate. A check every handler has to remember is a check some handler will eventually forget, and the forgotten one is the security bug. Mount this on the router group and handlers cannot opt out by accident.

func NewContext

func NewContext(ctx context.Context, o Owner) context.Context

NewContext returns a context carrying the owner.

Types

type Owner

type Owner struct {
	ID          string
	DisplayName string
	Email       string
}

Owner is the principal a request acts as.

The engine treats ID as opaque and never parses it. A wrapping application is free to make it a tenant UUID, an org slug, or anything else stable.

func FromContext

func FromContext(ctx context.Context) (Owner, bool)

FromContext returns the owner carried by ctx, if any.

func MustFromContext

func MustFromContext(ctx context.Context) Owner

MustFromContext returns the owner, panicking if absent.

Intended for handlers mounted behind Middleware, where a missing owner is a wiring bug rather than a runtime condition. Panicking makes that bug loud in development instead of silently serving another owner's data — which is the failure this whole seam exists to prevent.

func (Owner) Valid

func (o Owner) Valid() bool

Valid reports whether the owner is usable.

type Provider

type Provider interface {
	Resolve(ctx context.Context, r *http.Request) (Owner, error)
}

Provider resolves the owner for an inbound request.

Implementations must not write to the response; the middleware owns the failure path so that every rejection looks the same regardless of provider.

type ProviderFunc

type ProviderFunc func(ctx context.Context, r *http.Request) (Owner, error)

ProviderFunc adapts a function to Provider.

func (ProviderFunc) Resolve

func (f ProviderFunc) Resolve(ctx context.Context, r *http.Request) (Owner, error)

type SingleOwner

type SingleOwner struct {
	// contains filtered or unexported fields
}

SingleOwner resolves every request to the same owner without authenticating it.

This is the right default for the engine's primary use case — one person running it on their own machine or a private network — and the wrong choice for anything reachable from the internet. Pair it with StaticToken, a reverse proxy that authenticates, or a provider of your own for exposed deployments.

func NewSingleOwner

func NewSingleOwner(owner Owner) *SingleOwner

NewSingleOwner returns a provider that always resolves to owner. A zero-value owner falls back to a sensible local default.

func (*SingleOwner) Resolve

func (s *SingleOwner) Resolve(context.Context, *http.Request) (Owner, error)

type StaticToken

type StaticToken struct {
	// contains filtered or unexported fields
}

StaticToken authenticates a single owner with a shared bearer token.

Modest by design: it exists so a self-hoster can expose the engine without running it wide open, and so the Provider interface ships with more than one implementation — an interface with a single implementation tends not to stay a real seam.

func NewStaticToken

func NewStaticToken(owner Owner, token string) (*StaticToken, error)

NewStaticToken returns a provider that accepts exactly one bearer token. It returns an error rather than silently allowing everything if token is empty.

func (*StaticToken) Resolve

func (s *StaticToken) Resolve(_ context.Context, r *http.Request) (Owner, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL