Documentation
¶
Overview ¶
Package accessgatetable is the route table contract between the core renderer, which projects the plan's routes into it, and the StackKits Access Gate (internal/auth/accessgate), which decides from it (homelab identity target §7). It also names the headers the routers' gate middlewares carry. It holds no decision code and no credential handling, so the public CLI's renderer can depend on it.
Index ¶
Constants ¶
const ( HeaderListener = "Stackkit-Access-Listener" HeaderSubject = "Stackkit-Access-Subject" HeaderDevice = "Stackkit-Access-Device" HeaderAuthorization = "Authorization" HeaderDPoP = "DPoP" HeaderAgentEnvelope = "Stackkit-Agent-Envelope" // HeaderAppPass carries a device's app pass (homelab identity target // §6.3); the gate never returns it, so the router strips it. HeaderAppPass = "Stackkit-App-Pass" )
Request headers the gate reads and response headers it owns. Every name in ResponseHeaders is listed in the router's authResponseHeaders, so the router deletes a client-supplied copy before the backend sees the request and copies only the gate's value: the listener class and identity cannot be spoofed, and the per-request proof never reaches a backend. The device credential travels as "Authorization: DPoP <credential>" with its proof in "DPoP" (the carrier of internal/identity, RFC 9449); the gate hands an Authorization header of any other scheme back unchanged, so only a DPoP credential is removed.
const ( ListenerLAN = "lan" ListenerRemote = "remote" )
Listener classes. The class comes from the gate path the router calls.
const ( ExposureLocalOnly = "local-only" ExposureRemotePrivate = "remote-private" ExposurePublic = "public" )
Policy exposures of #AccessPolicyV2.
const ( AuthenticationNone = "none" AuthenticationHuman = "human" AuthenticationHumanDevice = "human+device" AuthenticationAgent = "agent" AuthenticationWorkload = "workload" )
Authentication classes. workload is a machine credential the endpoint verifies natively (the node /mcp token); it never admits a person.
const ( PrivilegeUser = "user" PrivilegeSignIn = "sign-in" PrivilegeAdmin = "admin" PrivilegeIdentity = "identity" PrivilegeSecrets = "secrets" PrivilegeVault = "vault" PrivilegeRecovery = "recovery" )
Privileges. sign-in labels the identity provider and forward-auth login surfaces, which sign-in and away enrollment need on every enabled path; identity is identity administration and stays on the LAN listener unless the Owner opts in.
const ( IngressForwardAuth = "forward-auth" IngressNative = "native" IngressNone = "none" )
Ingress authentication of the endpoint behind the route.
const DeviceProofEndpointPath = "/api/v1/identity/"
DeviceProofEndpointPath is the one route prefix whose endpoint verifies the device credential and proof itself.
const IdentityAdministrationPocketID = "pocketid"
IdentityAdministrationPocketID marks the sign-in route whose host also serves the Pocket ID administration surface.
const RoutesSchema = "stackkit.access-gate-routes/v1"
RoutesSchema identifies the compiler-owned route table the core renderer hands to the gate.
Variables ¶
var ( // HostPattern is a lowercase DNS name with at least two labels. HostPattern = regexp.MustCompile(`^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$`) // HTTPMethods are the request methods a route can serve, WebDAV, CalDAV // and CardDAV included. HTTPMethods = []string{ "CONNECT", "COPY", "DELETE", "GET", "HEAD", "LOCK", "MKCALENDAR", "MKCOL", "MOVE", "OPTIONS", "PATCH", "POST", "PROPFIND", "PROPPATCH", "PUT", "REPORT", "SEARCH", "TRACE", "UNLOCK", } )
var ErrInvalidTable = errors.New("accessgate: invalid route table")
ErrInvalidTable rejects a route table the gate cannot enforce exactly.
var LANBoundPrivileges = []string{PrivilegeAdmin, PrivilegeIdentity, PrivilegeSecrets, PrivilegeVault, PrivilegeRecovery}
LANBoundPrivileges stay on the LAN listener (or the VPN add-on) unless the Owner opts in (#AccessLANBoundPrivilegesV2).
var ResponseHeaders = []string{ HeaderListener, HeaderSubject, HeaderDevice, HeaderAuthorization, HeaderDPoP, HeaderAgentEnvelope, HeaderAppPass, "Remote-User", "Remote-Sub", "Remote-Name", "Remote-Email", "Remote-Groups", }
ResponseHeaders is the exact authResponseHeaders list of both gate middlewares. The TinyAuth identity headers are included so that on the remote listener, where TinyAuth is not consulted, a client cannot inject them.
Functions ¶
func HasDotSegment ¶
Types ¶
type Route ¶
type Route struct {
ID string `json:"id"`
Host string `json:"host"`
Path string `json:"path"`
ExactPath bool `json:"exactPath,omitempty"`
Exposure string `json:"exposure"`
Privilege string `json:"privilege"`
Authentication string `json:"authentication"`
IngressAuth string `json:"ingressAuth"`
OwnerRemoteOptIn bool `json:"ownerRemoteOptIn,omitempty"`
Groups []string `json:"groups,omitempty"`
AllowedMethods []string `json:"allowedMethods,omitempty"`
IdentityAdministration string `json:"identityAdministration,omitempty"`
AdministrationRemoteOptIn bool `json:"administrationRemoteOptIn,omitempty"`
AnonymousRateLimit *RateLimit `json:"anonymousRateLimit,omitempty"`
// AppAccessAway is the Owner's decision that the service's third-party
// apps reach this route on the remote listener with a per-device app pass
// issued by the Hub (homelab identity target §6.3); only a user route a
// person signs in to, reachable away from home, carries it.
AppAccessAway bool `json:"appAccessAway,omitempty"`
// DeviceProofEndpoint marks the Authority's own device identity routes
// (the Hub host's /api/v1/identity/): their endpoint verifies the device
// credential and DPoP proof itself, so the gate hands both through. Every
// other backend never sees them.
DeviceProofEndpoint bool `json:"deviceProofEndpoint,omitempty"`
}
Route is one compiler-owned route the gate decides for. Host and Path mirror the router rule: an exact host and a Traefik PathPrefix (plain string prefix) or, with ExactPath, a Path rule.
type Table ¶
type Table struct {
Schema string `json:"schema"`
// LANListener is true only on a server with a LAN listener (a Home
// core). A Cloud core's only listener is public, so its gate refuses
// the LAN path whoever calls it.
LANListener bool `json:"lanListener"`
Routes []Route `json:"routes"`
}
Table is the complete route table of one server.
func ParseTable ¶
ParseTable decodes and validates a route table strictly. A table the gate cannot enforce exactly is refused as a whole, so the router's forward-auth call fails and every route stays closed.