subjectresolve

package
v0.0.0-...-c805bba Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package subjectresolve resolves an agent-supplied user REFERENCE to a canonical platform user, server-side, against records the platform already trusts — never an agent-claimed mapping. Unknown schemes, malformed ids, and ambiguous joins are unresolved, fail-closed, with a human-readable reason the caller can relay.

A reference is handled by exactly one registered Resolver, tried in registration order: scheme-specific resolvers (email, trigger-author) are registered first and only claim a reference that is unambiguously theirs; the generic resource fallback is registered last and claims anything syntactically shaped like "<type>:<id>". Adding a new reference form is a new Resolver + a Register call — never a switch in a consumer — and Usages() derives the `get_preferences` tool's user-argument documentation from exactly the same registrations, so the two can never drift apart.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoRelationReader = errors.New("subjectresolve: no RelationReader configured")

ErrNoRelationReader is returned — deliberately as an ERROR, never a silent unresolved Resolution — when a well-formed "<type>:<id>" reference needs a RelationReader to resolve and env.Relations is nil. A caller with no RelationReader configured at all (rather than one that legitimately found no linked user) must not be told the SAME thing a resource with no sole_user is told; those are different facts; a caller that wants to PRESENT them the same way (e.g. an HTTP handler with no RelationReader wired, where an email-form reference should still work) must translate this sentinel itself via errors.Is, not rely on Resolve to blur it.

Functions

func Register

func Register(r Resolver)

Register adds a resolver, in call order. Panics (at init time, like sibling registries) when the resolver's Usage() reports an empty form or description — a resolver cannot exist undocumented — or when its form is already registered.

Types

type Env

type Env struct {
	// Relations reads one object's user-mapping relations from SpiceDB.
	Relations RelationReader
	// SessionAnnotations returns the owning AgentSession's annotations
	// (for session-derived references like trigger-author). Nil when no
	// session context exists.
	SessionAnnotations func(ctx context.Context) (map[string]string, error)
}

Env carries the injected capabilities resolvers may use.

type RelationReader

type RelationReader interface {
	// UserSubjects returns the bare canonical ids of the `user:` subjects on
	// the given object's relation. Resolution reads "sole_user" only — the
	// durable single-claimant authority — never the session-membership
	// "user" relation, which is legitimately multi-claimant and may be
	// mid-derivation.
	UserSubjects(ctx context.Context, objType, objID, relation string) ([]string, error)
}

RelationReader is the narrow SpiceDB surface the resource resolver needs.

type Resolution

type Resolution struct {
	// Subject is the bare canonical user id; empty when unresolved.
	Subject string
	// SubjectProven reports whether the resolver VOUCHES that the platform
	// links Subject to the reference — true only for a resolution backed by a
	// linkage authority (a resource's sole_user relation, trigger-author's
	// recursion into one). The email resolver leaves it false: it
	// canonicalizes any well-formed address into a subject in FORM only, with
	// nothing behind it, so a consumer must apply its own existence bar
	// before treating an unproven subject as a real platform user. The zero
	// value is unproven on purpose — a future resolver that forgets to claim
	// proof gets the stricter treatment, not the looser one.
	SubjectProven bool
	// Reason says why resolution failed, in words an agent may relay.
	Reason string
}

Resolution is the outcome of resolving one reference.

func Resolve

func Resolve(ctx context.Context, ref string, env Env) (Resolution, error)

Resolve resolves ref against every registered resolver, in registration order. The first resolver that claims ref (matched=true) commits the outcome, even when unresolved. A RelationReader/SessionAnnotations error from within a resolver is returned to the caller, never swallowed into an "unresolved" Resolution. When no resolver claims ref, it is rejected with a Reason naming the supported forms.

type Resolver

type Resolver interface {
	// Usage returns the reference form and a one-line description, exactly as
	// shown to the model in tool schemas (see Usages). Form is the literal
	// syntax (e.g. "email:<address>"); description says whom it resolves
	// (e.g. "the platform user with this verified email").
	Usage() (form, description string)

	// TryResolve attempts to resolve ref under this resolver's scheme.
	// matched=false means ref is outside this resolver's scheme entirely, and
	// Resolve tries the next registered resolver. matched=true commits to this
	// resolver's outcome (res, err) even when res is unresolved.
	TryResolve(ctx context.Context, ref string, env Env) (res Resolution, matched bool, err error)
}

Resolver is one reference-scheme handler, registered via Register.

type Usage

type Usage struct {
	Form        string
	Description string
}

Usage is one resolver's self-description, as surfaced by Usages().

func Usages

func Usages() []Usage

Usages lists every registered resolver's form and description, in registration order — specific schemes first, the generic fallback last. This is the single source `get_preferences`'s tool-schema documentation derives from; a new resolver is automatically reflected here.

Jump to

Keyboard shortcuts

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