Documentation
¶
Overview ¶
Package server runs the ppz HTTP API + GUI + embedded NATS.
Index ¶
- Constants
- Variables
- func MintOAuthState(key SessionKey, next string) (string, error)
- func Run(ctx context.Context, cfg Config) error
- func SignSessionCookie(key SessionKey, p SessionPayload) (string, error)
- func UserIDFromCtx(ctx context.Context) uuid.UUID
- type AccountPool
- type AuthedCaller
- type Config
- type OAuthState
- type OrgAccount
- type OrgRole
- type Server
- func (s *Server) JSFor(ctx context.Context, orgID uuid.UUID) (jetstream.JetStream, error)
- func (s *Server) NCFor(ctx context.Context, orgID uuid.UUID) (*nats.Conn, error)
- func (s *Server) RoleInOrg(ctx context.Context, userID, orgID uuid.UUID) (OrgRole, error)
- func (s *Server) Routes() *http.ServeMux
- type SessionKey
- type SessionPayload
Constants ¶
const SessionCookieName = "ppz_session"
Variables ¶
var ErrInvalidOAuthState = errors.New("invalid oauth state")
var ErrInvalidSession = errors.New("invalid session")
ErrInvalidSession is returned by VerifySessionCookie for any failure — malformed, wrong key, tampered, expired. Callers don't get to distinguish on purpose; "what went wrong" is leaky info.
Functions ¶
func MintOAuthState ¶
func MintOAuthState(key SessionKey, next string) (string, error)
MintOAuthState produces an HMAC-signed token containing a fresh random nonce + the desired post-auth `next` path. Rejects unsafe `next` values (anything that looks like an open-redirect away from this site).
func SignSessionCookie ¶
func SignSessionCookie(key SessionKey, p SessionPayload) (string, error)
Types ¶
type AccountPool ¶
type AccountPool struct {
// contains filtered or unexported fields
}
AccountPool is the per-org NATS account registry. Lazy: orgs are provisioned on first lookup, kept warm for the server's lifetime or until explicitly closed (org deletion).
func (*AccountPool) Close ¶
func (p *AccountPool) Close()
Close releases all per-org connections. Called on server shutdown.
func (*AccountPool) Drop ¶
func (p *AccountPool) Drop(orgID uuid.UUID)
Drop evicts the cached OrgAccount for orgID, closing the underlying NATS connection. Used by the dev-gated /api/v1/admin/simulate-stale- operator endpoint and (potentially) by future operations like org deletion.
func (*AccountPool) Forget ¶
func (p *AccountPool) Forget(orgID uuid.UUID)
Forget removes an org from the pool (e.g. on org deletion). The caller is responsible for any pre-close cleanup (draining streams etc.). This just closes the connection and clears the cache.
func (*AccountPool) Get ¶
func (p *AccountPool) Get(ctx context.Context, orgID uuid.UUID) (*OrgAccount, error)
Get returns the OrgAccount for orgID, provisioning it if needed. Provisioning steps (one-time per org):
- Generate fresh Account + signing keypairs
- MintAccountJWT signed by the Operator
- Persist (account_pub, account_jwt, signing_seed) on the org row
- Register the Account JWT with the live NATS resolver
- Mint a server-user JWT in the new account (broad perms)
- Open an in-process nats.Conn authenticated as that user
- Construct a JetStream client + cache the OrgAccount
Concurrent calls for the same org coalesce on the mutex; one goroutine provisions, the rest reuse the cached entry.
type AuthedCaller ¶
type AuthedCaller struct {
UserID uuid.UUID // set on OAuth path; uuid.Nil on API-key path (V1 keys aren't user-scoped)
APIKey *db.APIKey // populated when authed via api_keys
TokenID *uuid.UUID // populated when authed via oauth_tokens
}
AuthedCaller is what requireBearer attaches to the request context. Exactly one of APIKey or TokenID is populated.
func CallerFromCtx ¶
func CallerFromCtx(ctx context.Context) AuthedCaller
type Config ¶
type Config struct {
DBURL string
HTTPAddr string
NATSAddr string
NATSPublicURL string // returned to clients in /auth/exchange; defaults to ns.ClientURL()
SeedDir string
// SessionKey is the HMAC key used to sign session cookies. Read
// from PPZ_SESSION_KEY at boot. 32+ bytes recommended.
SessionKey []byte
// BaseURL is what the server thinks its own external URL is —
// used to construct OAuth redirect_uri. e.g.,
// "https://pipescloud.io" or "http://localhost:8080".
BaseURL string
// GitHub OAuth config. Real values come from .env.local (dev) or
// AWS Secrets Manager (prod). The three URL fields default to
// real-GitHub if empty; the e2e stack overrides them to point at
// the mock-github container.
GitHubClientID string
GitHubClientSecret string
GitHubAuthorizeURL string
GitHubTokenURL string
GitHubUserURL string
// DevLogin enables POST /dev/login?user=<seed-user> for tests.
// MUST be false in production.
DevLogin bool
// Auth V2 §Phase 3.5 — NSC/JWT decentralized NATS auth.
// Operator seed is the runtime secret (signs new per-org
// Account JWTs as orgs are provisioned). Operator JWT is
// public — declares the operator's existence to the embedded
// NATS server. System Account JWT lives in /sys for JetStream.
NATSOperatorSeed string
NATSOperatorJWT string
NATSSystemAccountJWT string
}
type OAuthState ¶
OAuthState is what Verify returns on success.
func VerifyOAuthState ¶
func VerifyOAuthState(key SessionKey, token string) (OAuthState, error)
type OrgAccount ¶
type OrgAccount struct {
OrgID uuid.UUID
OrgName string
AccountPub string
// SigningKP is loaded from the per-org signing seed on first
// access; used to mint per-(user, org) JWTs in /auth/exchange.
SigningKP nkeys.KeyPair
// NC is the in-process server-user connection — broad pub/sub
// in this account. Used by handlers for JetStream management
// and by the broadcast-subscriber.
NC *nats.Conn
JS jetstream.JetStream
// contains filtered or unexported fields
}
OrgAccount is the runtime state ppz-server holds for one provisioned org's NATS account. Closed when the org is deleted.
type Server ¶
type Server struct {
Pool *db.Pool
NATSURL string
SessionKey SessionKey
// Phase 3.5 — per-org account pool. Lazily provisions an
// Operator-signed Account JWT per org on first use, opens a
// per-account in-process connection, registers the JWT with
// the live resolver. AccountPool.Get(orgID) is the path to
// per-org NATS state. (No legacy single-account fallback —
// every NATS operation routes through the pool.)
AccountPool *AccountPool
OperatorKP nkeys.KeyPair // hot at runtime; signs new Account JWTs
NATSResolver natsserver.AccountResolver // for runtime account JWT registration
NATSClientURL string // for AccountPool.openAccount
BaseURL string
GitHubClientID string
GitHubClientSecret string
GitHubAuthorizeURL string
GitHubTokenURL string
GitHubUserURL string
DevLogin bool
}
Server holds the shared dependencies threaded through HTTP handlers.
func (*Server) JSFor ¶
JSFor is a thin convenience wrapper handlers use to fetch the per-org JetStream client without dragging the full *OrgAccount through. Calls AccountPool.Get under the hood, so the org is provisioned on first use.
func (*Server) NCFor ¶
NCFor returns the per-org NATS connection — used by handlers that need raw pub/sub (e.g. terminal WebSocket subscribers).
type SessionPayload ¶
SessionPayload is what we encode into the cookie value (and what VerifySessionCookie returns on success).
func VerifySessionCookie ¶
func VerifySessionCookie(key SessionKey, cookieValue string) (SessionPayload, error)
Source Files
¶
- account_pool.go
- assets.go
- auth_bearer.go
- auth_session.go
- broadcast_subscriber.go
- handlers_admin.go
- handlers_api.go
- handlers_auth.go
- handlers_channel.go
- handlers_device_flow.go
- handlers_gui.go
- handlers_owner_gates.go
- handlers_terminal.go
- handlers_users.go
- http.go
- natsembed.go
- oauth_state.go
- role.go
- server.go
- session.go
- streams.go