server

package
v0.17.8 Latest Latest
Warning

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

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

Documentation

Overview

Package server runs the ppz HTTP API + GUI + embedded NATS.

Index

Constants

View Source
const SessionCookieName = "ppz_session"

Variables

View Source
var ErrInvalidOAuthState = errors.New("invalid oauth state")
View Source
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 Run

func Run(ctx context.Context, cfg Config) error

func SignSessionCookie

func SignSessionCookie(key SessionKey, p SessionPayload) (string, error)

func UserIDFromCtx

func UserIDFromCtx(ctx context.Context) uuid.UUID

UserIDFromCtx returns the authenticated user_id, or uuid.Nil if the request was not gated by requireSession.

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):

  1. Generate fresh Account + signing keypairs
  2. MintAccountJWT signed by the Operator
  3. Persist (account_pub, account_jwt, signing_seed) on the org row
  4. Register the Account JWT with the live NATS resolver
  5. Mint a server-user JWT in the new account (broad perms)
  6. Open an in-process nats.Conn authenticated as that user
  7. 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

type OAuthState struct {
	Nonce string
	Next  string
}

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 OrgRole

type OrgRole string

OrgRole is the role the user has in an org. "" = not a member.

const (
	OrgRoleOwner  OrgRole = "owner"
	OrgRoleMember OrgRole = "member"
	OrgRoleNone   OrgRole = ""
)

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

func (s *Server) JSFor(ctx context.Context, orgID uuid.UUID) (jetstream.JetStream, error)

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

func (s *Server) NCFor(ctx context.Context, orgID uuid.UUID) (*nats.Conn, error)

NCFor returns the per-org NATS connection — used by handlers that need raw pub/sub (e.g. terminal WebSocket subscribers).

func (*Server) RoleInOrg

func (s *Server) RoleInOrg(ctx context.Context, userID, orgID uuid.UUID) (OrgRole, error)

RoleInOrg returns the calling user's role in the given org.

  • OrgRoleOwner if users.id == organisations.owner_user_id
  • OrgRoleMember if listed in organisation_members (and not owner)
  • OrgRoleNone otherwise

func (*Server) Routes

func (s *Server) Routes() *http.ServeMux

type SessionKey

type SessionKey []byte

SessionKey is the HMAC signing key. 32+ bytes recommended.

type SessionPayload

type SessionPayload struct {
	UserID    uuid.UUID
	ExpiresAt time.Time
}

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)

Jump to

Keyboard shortcuts

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