Help us shape the future of Go! Take the 2026 Go Developer Survey

service

package
v0.1.0-alpha.1 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2026 License: Apache-2.0, AGPL-3.0 Imports: 22 Imported by: 0

Documentation

Overview

Package service holds Headmesh's use cases — enrollment, grants, API keys, bootstrap — on top of the store and the cell hub. HTTP handlers in package api are thin wrappers around it.

Index

Constants

View Source
const (
	PrefixEnrollmentToken = "hmj_"
	PrefixAPIKey          = "hmk_"
	PrefixCompletion      = "hmc_"
)

Secret prefixes make leaked credentials recognisable to secret scanners and humans.

View Source
const (
	ScopeAdmin        = "admin"
	ScopeDevicesRead  = "devices:read"
	ScopeEnrollWrite  = "enroll:write"
	ScopeGrantsWrite  = "grants:write"
	ScopeGrantsRead   = "grants:read"
	ScopeAPIKeysWrite = "apikeys:write"
)

Scopes.

Variables

View Source
var (
	ErrNotFound      = errors.New("not found")
	ErrUnauthorized  = errors.New("unauthorized")
	ErrForbidden     = errors.New("forbidden")
	ErrInvalid       = errors.New("invalid request")
	ErrConflict      = errors.New("conflict")
	ErrUnavailable   = errors.New("cell unavailable")
	ErrTokenRejected = errors.New("enrollment token is invalid, expired, revoked or used up")
)

Errors returned to callers; the API maps them to HTTP status codes.

Functions

This section is empty.

Types

type APIKey

type APIKey struct {
	ID        string
	Key       string // only set on creation
	Prefix    string
	Name      string
	Scopes    []string
	ExpiresAt *time.Time
}

APIKey is returned once, at creation.

type BootstrapRequest

type BootstrapRequest struct {
	CellName, ServerURL, BaseDomain, CellMode string
	OrgSlug, OrgName, Template                string
	Platform                                  bool
	ParentSlug                                string
	HeadscaleUsers                            []string
}

BootstrapRequest creates a cell (if missing), an org in it and an admin API key. It is how an operator sets up a fresh installation from the command line before any user accounts exist.

type BootstrapResult

type BootstrapResult struct {
	CellID, OrgID, OrgShort string
	AdminKey                *APIKey
}

BootstrapResult reports what was created. AdminKey is shown once.

type CompleteRequest

type CompleteRequest struct {
	EnrollmentID     string
	CompletionSecret string
	NodeID           uint64
}

CompleteRequest binds the registered Headscale node to the enrollment.

type Device

type Device struct {
	ID         string
	ShortID    string
	Name       string
	GivenName  string
	Kind       string
	Roles      []string
	IPv4, IPv6 string
	Online     bool
	State      string
}

Device is the API view of a device.

type EnrollRequest

type EnrollRequest struct {
	Token    string
	Hostname string
	OS       string
	Arch     string
	ClientIP *netip.Addr
}

EnrollRequest is what a joining device sends. The token is its only credential.

type EnrollResponse

type EnrollResponse struct {
	EnrollmentID     string
	DeviceID         string
	LoginServer      string
	AuthKey          string
	Hostname         string
	Tags             []string
	ExpiresAt        time.Time
	CompletionSecret string
}

EnrollResponse is everything the client needs to run `tailscale up`.

type Grant

type Grant struct {
	ID          string
	GrantorOrg  string
	GranteeOrg  string
	DstDeviceID string
	Ports       []string
	StartsAt    time.Time
	ExpiresAt   time.Time
	Revoked     bool
	ExternalRef string
	// Enforced is true when the cell confirmed the resulting policy is live.
	Enforced bool
}

Grant is the API view of a grant.

type GrantRequest

type GrantRequest struct {
	GranteeOrg  string // short id of the org receiving access
	Src         store.SelectorJSON
	DstDeviceID string
	Ports       []string
	StartsAt    *time.Time
	ExpiresAt   time.Time
	Reason      string
	ExternalRef string // idempotency key, for example a rental id
}

GrantRequest gives devices of another org time-limited access to one device of the principal's org.

type Hub

type Hub interface {
	Refresh(ctx context.Context, cell string) error
	RefreshAndWait(ctx context.Context, cell string) error
	Do(ctx context.Context, cell string, op *cellv1.Op) (*cellv1.OpResult, error)
}

Hub is the part of the cell hub services use.

type Principal

type Principal struct {
	KeyID    pgtype.UUID
	OrgID    pgtype.UUID
	OrgShort string
	Scopes   []string
}

Principal is an authenticated API caller acting for one org.

func (Principal) Can

func (p Principal) Can(scope string) bool

Can reports whether the principal holds scope (admin implies every scope).

type RecoveryHub

type RecoveryHub interface {
	Connected(cell string) bool
	RefreshAndWait(ctx context.Context, cell string) error
}

RecoveryHub is the part of the hub the startup gate uses.

type Service

type Service struct {
	Store *store.Store
	Hub   Hub
	Now   func() time.Time
	// MaxGrantTTL caps grant lifetimes unless an org setting is lower (default 30 days).
	MaxGrantTTL time.Duration
	// PublicURL is the API's external base URL, used in join links (for example https://api.headmesh.dev).
	PublicURL string
	// InstallURL is where the install script is served (for example https://get.headmesh.dev).
	InstallURL string
}

Service bundles the use cases.

func (*Service) Authenticate

func (s *Service) Authenticate(ctx context.Context, rawKey string) (Principal, error)

Authenticate resolves an API key to a principal. Lookup runs in the system scope because the key is what establishes the tenant scope; nothing else is read there.

func (*Service) AwaitCellsCurrent

func (s *Service) AwaitCellsCurrent(ctx context.Context, hub RecoveryHub, log *slog.Logger) error

AwaitCellsCurrent blocks until every enforce-mode cell is connected and has applied a policy compiled now, so grants that expired while the control plane was down are closed before the API accepts new work (plan §13.4). Observe-mode cells never block. It returns an error if ctx ends first.

func (*Service) Bootstrap

func (s *Service) Bootstrap(ctx context.Context, req BootstrapRequest) (*BootstrapResult, error)

Bootstrap runs in the system scope; it is only reachable from the server binary, never over HTTP.

func (*Service) CompleteEnrollment

func (s *Service) CompleteEnrollment(ctx context.Context, req CompleteRequest) (*Device, error)

CompleteEnrollment checks the caller holds the completion secret, asks the cell which pre-auth key the node registered with, and only then binds the node to the device and gives it its MagicDNS name.

func (*Service) CreateAPIKey

func (s *Service) CreateAPIKey(ctx context.Context, p Principal, name string, scopes []string, ttl time.Duration) (*APIKey, error)

CreateAPIKey issues a key for the principal's org. A key can never grant scopes its creator lacks.

func (*Service) CreateEnrollmentToken

func (s *Service) CreateEnrollmentToken(ctx context.Context, p Principal, req TokenRequest) (*TokenResponse, error)

CreateEnrollmentToken mints an invite for devices joining the principal's org.

func (*Service) CreateGrant

func (s *Service) CreateGrant(ctx context.Context, p Principal, req GrantRequest) (*Grant, error)

CreateGrant creates (or, with the same external ref, returns) a grant and pushes the new policy to the cell.

func (*Service) Enroll

func (s *Service) Enroll(ctx context.Context, req EnrollRequest) (*EnrollResponse, error)

Enroll spends one use of an enrollment token, creates the device, makes sure the cell's live policy already declares the device's tags, and returns a single-use 5-minute pre-auth key carrying them.

func (*Service) ListDevices

func (s *Service) ListDevices(ctx context.Context, p Principal) ([]*Device, error)

ListDevices returns the principal's org devices.

func (*Service) ListGrants

func (s *Service) ListGrants(ctx context.Context, p Principal) ([]*Grant, error)

ListGrants returns active grants the principal's org issued or received.

func (*Service) RevokeGrant

func (s *Service) RevokeGrant(ctx context.Context, p Principal, grantID string) (*Grant, error)

RevokeGrant revokes a grant the principal's org issued and waits until the cell enforces it.

func (*Service) SweepExpiredGrants

func (s *Service) SweepExpiredGrants(ctx context.Context) (int, error)

SweepExpiredGrants writes one grant.expired audit entry for every grant whose expiry has passed and has not been audited, recording how late the sweep noticed it. Access itself already ended on time — cells apply precomputed policy revisions at each expiry — so this is bookkeeping, not enforcement. It returns how many grants it audited.

type TokenRequest

type TokenRequest struct {
	Roles            []string
	DeviceKind       string
	MaxUses          int32
	TTL              time.Duration
	Ephemeral        bool
	ApprovalRequired bool
}

TokenRequest creates an enrollment token for the principal's org.

type TokenResponse

type TokenResponse struct {
	ID        string
	Token     string
	JoinURL   string
	Commands  map[string]string
	ExpiresAt time.Time
}

TokenResponse carries the raw token exactly once, with ready-to-paste join commands.

Jump to

Keyboard shortcuts

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