Documentation
¶
Index ¶
- Constants
- func DeriveKey(master, purpose string) []byte
- func FormatUptime(since time.Time) string
- type AuditEntry
- type AuditLog
- type ClientView
- type EpochFunc
- type Flash
- type Handlers
- type HostCert
- type PageData
- type PasskeyView
- type SessionManager
- func (sm *SessionManager) CSRFToken(r *http.Request) string
- func (sm *SessionManager) ClearSession(w http.ResponseWriter)
- func (sm *SessionManager) CreateSession(w http.ResponseWriter, userID, role string) error
- func (sm *SessionManager) GetActor(r *http.Request) string
- func (sm *SessionManager) IsAdmin(r *http.Request) bool
- func (sm *SessionManager) RefreshSession(w http.ResponseWriter, r *http.Request) error
- func (sm *SessionManager) Role(r *http.Request) string
- func (sm *SessionManager) SetEpochSource(fn EpochFunc)
- func (sm *SessionManager) UserID(r *http.Request) string
- func (sm *SessionManager) ValidCSRFToken(r *http.Request, token string) bool
- func (sm *SessionManager) ValidateSession(r *http.Request) bool
- type StoredCredential
- type TunnelSession
- type TunnelView
- type User
- type UserStore
- func (s *UserStore) AddCredential(userID string, c StoredCredential) error
- func (s *UserStore) All() []*User
- func (s *UserStore) BootstrapInvite() (string, error)
- func (s *UserStore) BumpEpoch(id string) error
- func (s *UserStore) Create(name, role string) (id, inviteToken string, err error)
- func (s *UserStore) Delete(id string) error
- func (s *UserStore) Epoch(id string) (int, bool)
- func (s *UserStore) FindByCredentialID(credID []byte) *User
- func (s *UserStore) FindByInvite(token string) *User
- func (s *UserStore) Get(id string) *User
- func (s *UserStore) NeedsSetup() bool
- func (s *UserStore) RemoveCredential(userID string, credID []byte) error
- func (s *UserStore) Rename(id, name string) error
- func (s *UserStore) Reset(id string) (inviteToken string, err error)
- func (s *UserStore) SetRole(id, role string) error
- func (s *UserStore) UpdateSignCount(userID string, credID []byte, count uint32)
- type WebAuthnHandler
- func (h *WebAuthnHandler) BeginLogin() (assertion interface{}, challengeID string, err error)
- func (h *WebAuthnHandler) BeginRegistration(u *User) (creation interface{}, challengeID string, err error)
- func (h *WebAuthnHandler) FinishLogin(r *http.Request, challengeID string) (userID string, credID []byte, signCount uint32, err error)
- func (h *WebAuthnHandler) FinishRegistration(r *http.Request, challengeID string) (userID string, sc StoredCredential, err error)
- func (h *WebAuthnHandler) NeedsSetup() bool
Constants ¶
const ( RoleAdmin = "admin" RoleUser = "user" )
User roles.
Variables ¶
This section is empty.
Functions ¶
func DeriveKey ¶ added in v0.8.8
DeriveKey derives a purpose-specific key from a master secret using HMAC-SHA256.
func FormatUptime ¶
FormatUptime returns a human-readable duration string.
Types ¶
type AuditEntry ¶ added in v0.8.0
type AuditEntry struct {
Timestamp time.Time `json:"timestamp"`
Actor string `json:"actor"`
Action string `json:"action"`
Detail string `json:"detail"`
}
AuditEntry represents a single audit log event.
type AuditLog ¶ added in v0.8.0
type AuditLog struct {
// contains filtered or unexported fields
}
AuditLog is an append-only NDJSON audit log: one JSON object per line, appended with O(1) cost (no whole-file rewrites). The most recent entries are also kept in memory (newest first) to serve the admin UI cheaply.
func NewAuditLog ¶ added in v0.8.0
NewAuditLog loads or creates an audit log at the given path. A legacy JSON-array file (from older versions) is transparently migrated to NDJSON.
func (*AuditLog) Count ¶ added in v0.8.0
Count returns the number of recent entries available to the UI.
func (*AuditLog) Entries ¶ added in v0.8.0
func (a *AuditLog) Entries(limit, offset int) []AuditEntry
Entries returns recent audit entries (newest first) with offset-based pagination, served from the in-memory ring.
func (*AuditLog) Facets ¶ added in v0.8.19
Facets returns the distinct actors and actions present in the window, sorted, for populating filter controls.
func (*AuditLog) Log ¶ added in v0.8.0
Log records a new audit event. Newest entries are first in the UI.
func (*AuditLog) Query ¶ added in v0.8.19
func (a *AuditLog) Query(limit, offset int, actor, action string) (entries []AuditEntry, total int)
Query returns recent entries (newest first) matching the optional actor and action filters (empty = any), with offset-based pagination, plus the total number of matching entries in the window (for pagination).
type ClientView ¶
type ClientView struct {
Addr string
Uptime string // human-readable, e.g. "2h 15m"
Version string
}
ClientView holds display info for a single connected client.
type EpochFunc ¶ added in v0.9.2
EpochFunc returns a user's current session revocation epoch and whether the user still exists. Sessions whose embedded epoch is older than the current one — or whose user no longer exists — are rejected, giving real server-side revocation on logout, passkey reset, role change, and deletion. A nil EpochFunc disables the check (used in tests without a user store).
type Handlers ¶
type Handlers struct {
// contains filtered or unexported fields
}
Handlers holds the admin panel HTTP handlers.
func NewHandlers ¶
NewHandlers creates admin panel handlers.
func (*Handlers) Render ¶
func (h *Handlers) Render(w http.ResponseWriter, page string, data *PageData)
Render renders a page template inside the base layout. The page parameter is the template path, e.g. "pages/login.html".
func (*Handlers) RenderPartial ¶
func (h *Handlers) RenderPartial(w http.ResponseWriter, page, block string, data any)
RenderPartial renders a named template block without the base layout.
type HostCert ¶
type HostCert struct {
Hostname string
Valid bool
Expiry string // formatted date, e.g. "May 15, 2026"
Error string // non-empty when cert is missing or invalid
}
HostCert holds TLS certificate status for a single hostname.
type PageData ¶
type PageData struct {
Title string
Active string
Version string
CheckIntervalMS int64
CSRFToken string
Flash *Flash
Data any
UserID string // logged-in user's opaque id
Name string // logged-in user's display label
IsAdmin bool // logged-in user is an admin (drives nav gating)
}
PageData is the template rendering context.
type PasskeyView ¶
type PasskeyView struct {
Name string
CreatedAt string
LastUsedAt string
IDB64 string // base64url-encoded credential ID
}
PasskeyView represents a passkey for display in templates.
type SessionManager ¶
type SessionManager struct {
// contains filtered or unexported fields
}
SessionManager handles JWT-based sessions.
func NewSessionManager ¶
func NewSessionManager(secret string) *SessionManager
NewSessionManager creates a new session manager with a purpose-derived key.
func (*SessionManager) CSRFToken ¶
func (sm *SessionManager) CSRFToken(r *http.Request) string
CSRFToken generates a CSRF token bound to the session's stable identity, not the (frequently re-issued) signed cookie value. New sessions bind to the SID; legacy sessions without a SID fall back to the actor. Either way the token is stable across keepalive re-issues, so it stays valid across open tabs. Returns empty string if there is no valid session.
func (*SessionManager) ClearSession ¶
func (sm *SessionManager) ClearSession(w http.ResponseWriter)
ClearSession removes the session cookie.
func (*SessionManager) CreateSession ¶
func (sm *SessionManager) CreateSession(w http.ResponseWriter, userID, role string) error
CreateSession starts a new session (fresh SID) for a login as the given user, stamping it with the user's current revocation epoch.
func (*SessionManager) GetActor ¶ added in v0.8.0
func (sm *SessionManager) GetActor(r *http.Request) string
GetActor returns the user ID from the session JWT (for the audit log).
func (*SessionManager) IsAdmin ¶ added in v0.9.0
func (sm *SessionManager) IsAdmin(r *http.Request) bool
IsAdmin reports whether the request has a valid admin session.
func (*SessionManager) RefreshSession ¶ added in v0.8.17
func (sm *SessionManager) RefreshSession(w http.ResponseWriter, r *http.Request) error
RefreshSession re-issues the current session with a bumped expiry, PRESERVING its user, role, and SID. Used by the sliding-window keepalive so the CSRF token (bound to the SID) does not change underneath other open tabs.
func (*SessionManager) Role ¶ added in v0.9.0
func (sm *SessionManager) Role(r *http.Request) string
Role returns the logged-in user's role, or "" if no valid session.
func (*SessionManager) SetEpochSource ¶ added in v0.9.2
func (sm *SessionManager) SetEpochSource(fn EpochFunc)
SetEpochSource wires the revocation-epoch lookup (typically UserStore.Epoch).
func (*SessionManager) UserID ¶ added in v0.9.0
func (sm *SessionManager) UserID(r *http.Request) string
UserID returns the logged-in user's ID, or "" if no valid session.
func (*SessionManager) ValidCSRFToken ¶
func (sm *SessionManager) ValidCSRFToken(r *http.Request, token string) bool
ValidCSRFToken checks that a submitted CSRF token matches the session.
func (*SessionManager) ValidateSession ¶
func (sm *SessionManager) ValidateSession(r *http.Request) bool
ValidateSession checks if the request has a valid session.
type StoredCredential ¶
type StoredCredential struct {
ID []byte `json:"id"`
PublicKey []byte `json:"public_key"`
Name string `json:"name"`
AAGUID []byte `json:"aaguid"`
SignCount uint32 `json:"sign_count"`
CreatedAt time.Time `json:"created_at"`
LastUsedAt time.Time `json:"last_used_at"`
Transport []string `json:"transport"`
AttType string `json:"att_type"`
BackupEligible bool `json:"backup_eligible"`
BackupState bool `json:"backup_state"`
}
StoredCredential is a single WebAuthn passkey credential on disk.
type TunnelSession ¶ added in v0.9.0
type TunnelSession struct {
// contains filtered or unexported fields
}
TunnelSession manages per-hostname authenticated sessions for protected tunnels, established via the cross-host handoff from the admin login.
func NewTunnelSession ¶ added in v0.9.0
func NewTunnelSession(secret string) *TunnelSession
NewTunnelSession creates a tunnel session manager with a purpose-derived key.
func (*TunnelSession) CreateSession ¶ added in v0.9.0
func (t *TunnelSession) CreateSession(w http.ResponseWriter, hostname, userID, role string) error
CreateSession sets a tunnel session cookie scoped to the hostname, recording the authenticated user.
func (*TunnelSession) SetEpochSource ¶ added in v0.9.2
func (t *TunnelSession) SetEpochSource(fn EpochFunc)
SetEpochSource wires the revocation-epoch lookup (typically UserStore.Epoch), so logout / passkey reset / role change / deletion also kill tunnel sessions.
func (*TunnelSession) ValidateSession ¶ added in v0.9.0
func (t *TunnelSession) ValidateSession(r *http.Request, hostname string) (userID, role string, ok bool)
ValidateSession returns the authenticated user for a tunnel hostname, if any.
type TunnelView ¶
type TunnelView struct {
ID string
Type string
Hostnames []string
ListenPort int
PreserveHost bool
TLSPassthrough bool
IPPolicy string
AuthPolicy string
Connected bool
ClientCount int
Clients []ClientView
Requests int64
BytesIn int64
BytesOut int64
ActiveConns int32
Token string
HostCerts []HostCert
ServerVersion string
}
TunnelView is the template data for a single tunnel row.
func (TunnelView) BytesInFmt ¶
func (t TunnelView) BytesInFmt() string
BytesInFmt formats bytes in as a human-readable string.
func (TunnelView) BytesOutFmt ¶
func (t TunnelView) BytesOutFmt() string
BytesOutFmt formats bytes out as a human-readable string.
func (TunnelView) ClientSummary ¶
func (t TunnelView) ClientSummary() string
ClientSummary returns a tooltip-friendly summary of all clients with uptime and version. The output is sorted by address for a stable, deterministic tooltip.
func (TunnelView) HostnamesCSV ¶
func (t TunnelView) HostnamesCSV() string
HostnamesCSV returns hostnames as a comma-separated string.
func (TunnelView) IsClientOutdated ¶ added in v0.8.1
func (t TunnelView) IsClientOutdated() bool
IsClientOutdated returns true if any connected client has a version different from the server.
type User ¶ added in v0.9.0
type User struct {
ID string `json:"id"`
Name string `json:"name"`
Role string `json:"role"`
Credentials []StoredCredential `json:"credentials"`
InviteToken string `json:"invite_token,omitempty"`
InviteExpires time.Time `json:"invite_expires,omitempty"`
CreatedAt time.Time `json:"created_at"`
// SessionEpoch is a monotonically increasing revocation counter. Every issued
// session JWT carries the epoch current at mint time; a session whose epoch is
// older than this is rejected. Bumping it (logout, passkey reset, role change)
// invalidates all of the user's outstanding sessions across devices, which is
// what makes logout and credential reset actually revoke a stolen cookie.
SessionEpoch int `json:"session_epoch,omitempty"`
}
User is a member of the gatecrash directory. The ID is an opaque, immutable GUID — it is what passkeys, sessions, and access policies bind to, so it can never change. Name is a free, renameable display label. Users authenticate with one or more passkeys.
func (*User) CredentialName ¶ added in v0.9.3
CredentialName returns the label of the credential with the given ID, or "" if the user has no such credential. Used to record which passkey was used.
func (*User) HasPasskeys ¶ added in v0.9.0
type UserStore ¶ added in v0.9.0
type UserStore struct {
// contains filtered or unexported fields
}
UserStore persists the user directory to a JSON file. It is safe for concurrent use.
func NewUserStore ¶ added in v0.9.0
NewUserStore loads or creates a user store at the given path.
func (*UserStore) AddCredential ¶ added in v0.9.0
func (s *UserStore) AddCredential(userID string, c StoredCredential) error
AddCredential appends a passkey to a user and consumes any pending invite.
func (*UserStore) All ¶ added in v0.9.0
All returns copies of every user, sorted by ID (admins first).
func (*UserStore) BootstrapInvite ¶ added in v0.9.1
BootstrapInvite ensures there is a first admin awaiting passkey registration and returns its active invite token. It returns "" when an admin already has a passkey (already initialized — no bootstrap needed). Idempotent: it reuses an existing pending bootstrap admin and a still-valid invite, only minting a new token when none is active. This replaces an open first-login page: the only way to claim the first admin is the link printed to the logs / config file.
func (*UserStore) BumpEpoch ¶ added in v0.9.2
BumpEpoch increments a user's session epoch, invalidating all of their outstanding sessions, and persists it. A missing user is a no-op.
func (*UserStore) Create ¶ added in v0.9.0
Create adds a new user with the given display name and role, assigning an opaque GUID id, and issues an invite token. Returns the new id and the plaintext invite token (shown once).
func (*UserStore) Epoch ¶ added in v0.9.2
Epoch returns a user's current session revocation epoch and whether the user exists. It is the source consulted by SessionManager/TunnelSession to reject stale (logged-out / reset / re-roled) or deleted-user sessions.
func (*UserStore) FindByCredentialID ¶ added in v0.9.0
FindByCredentialID returns the user who owns the given credential (for usernameless/discoverable login).
func (*UserStore) FindByInvite ¶ added in v0.9.0
FindByInvite returns the user holding a matching, unexpired invite token.
func (*UserStore) NeedsSetup ¶ added in v0.9.0
NeedsSetup reports whether the directory has no admin yet (first boot).
func (*UserStore) RemoveCredential ¶ added in v0.9.0
RemoveCredential deletes a passkey from a user. It won't remove the last passkey of the last admin (which would lock everyone out).
func (*UserStore) Rename ¶ added in v0.9.1
Rename changes a user's display label. The id is immutable.
func (*UserStore) Reset ¶ added in v0.9.0
Reset clears a user's passkeys and issues a fresh invite token.
type WebAuthnHandler ¶
type WebAuthnHandler struct {
// contains filtered or unexported fields
}
WebAuthnHandler runs passkey registration and (usernameless) login ceremonies against the user directory. It is transport-agnostic: callers handle HTTP and persistence.
func NewWebAuthnHandler ¶
func NewWebAuthnHandler(rpID, rpOrigin string, users *UserStore) (*WebAuthnHandler, error)
NewWebAuthnHandler creates a WebAuthn handler bound to the user directory.
func (*WebAuthnHandler) BeginLogin ¶ added in v0.9.0
func (h *WebAuthnHandler) BeginLogin() (assertion interface{}, challengeID string, err error)
BeginLogin starts a usernameless (discoverable) login.
func (*WebAuthnHandler) BeginRegistration ¶ added in v0.9.0
func (h *WebAuthnHandler) BeginRegistration(u *User) (creation interface{}, challengeID string, err error)
BeginRegistration starts a passkey registration for the target user, requiring a discoverable (resident) credential so usernameless login works.
func (*WebAuthnHandler) FinishLogin ¶ added in v0.9.0
func (h *WebAuthnHandler) FinishLogin(r *http.Request, challengeID string) (userID string, credID []byte, signCount uint32, err error)
FinishLogin verifies a discoverable login, resolving and returning the authenticated user ID, the credential used, and its new sign count.
func (*WebAuthnHandler) FinishRegistration ¶ added in v0.9.0
func (h *WebAuthnHandler) FinishRegistration(r *http.Request, challengeID string) (userID string, sc StoredCredential, err error)
FinishRegistration verifies the registration response and returns the target user ID and the new credential. The caller persists it.
func (*WebAuthnHandler) NeedsSetup ¶
func (h *WebAuthnHandler) NeedsSetup() bool
NeedsSetup reports whether first-boot admin provisioning is required.