accessgatetable

package
v0.55.16 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

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

View Source
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.

View Source
const (
	ListenerLAN    = "lan"
	ListenerRemote = "remote"
)

Listener classes. The class comes from the gate path the router calls.

View Source
const (
	ExposureLocalOnly     = "local-only"
	ExposureRemotePrivate = "remote-private"
	ExposurePublic        = "public"
)

Policy exposures of #AccessPolicyV2.

View Source
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.

View Source
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.

View Source
const (
	IngressForwardAuth = "forward-auth"
	IngressNative      = "native"
	IngressNone        = "none"
)

Ingress authentication of the endpoint behind the route.

View Source
const DeviceProofEndpointPath = "/api/v1/identity/"

DeviceProofEndpointPath is the one route prefix whose endpoint verifies the device credential and proof itself.

View Source
const IdentityAdministrationPocketID = "pocketid"

IdentityAdministrationPocketID marks the sign-in route whose host also serves the Pocket ID administration surface.

View Source
const RoutesSchema = "stackkit.access-gate-routes/v1"

RoutesSchema identifies the compiler-owned route table the core renderer hands to the gate.

Variables

View Source
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",
	}
)
View Source
var ErrInvalidTable = errors.New("accessgate: invalid route table")

ErrInvalidTable rejects a route table the gate cannot enforce exactly.

LANBoundPrivileges stay on the LAN listener (or the VPN add-on) unless the Owner opts in (#AccessLANBoundPrivilegesV2).

View Source
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

func HasDotSegment(path string) bool

Types

type RateLimit

type RateLimit struct {
	Requests      int `json:"requests"`
	WindowSeconds int `json:"windowSeconds"`
}

RateLimit is the anonymous request budget of a public route without login.

func (*RateLimit) Valid

func (l *RateLimit) Valid() bool

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

func ParseTable(raw []byte) (Table, error)

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.

func (Table) Lookup

func (t Table) Lookup(host, path string) (Route, bool)

lookup returns the route the router selected for host and path: an exact host, then the longest matching Path or PathPrefix rule.

func (Table) Validate

func (t Table) Validate() error

Validate enforces the #AccessPolicyV2 invariants on every route, so a stale or hand-edited table cannot widen access.

Jump to

Keyboard shortcuts

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