Documentation
¶
Overview ¶
Package natsauth implements Auth V2 Phase 3: decentralized NATS auth via per-Account JWTs (the "NSC model" — see docs/AUTH-V2.md §Phase 3).
At runtime ppz-server holds a single Account signing key. On /auth/exchange it generates a fresh user nkey pair, mints a User JWT with subject permissions scoped to the caller's org, and hands (jwt, seed) back to the daemon. NATS validates the chain locally — no callback to ppz-server in the data path.
Index ¶
- func MintAccountJWT(operatorKP nkeys.KeyPair, accountName string, ...) (string, error)
- func MintUserJWTInAccount(accountPub string, signingKP nkeys.KeyPair, name string, ...) (jwtStr, seed string, err error)
- func StartEmbeddedNATSWithAuth(cfg EmbeddedConfig) (*natsserver.Server, error)
- type Account
- type EmbeddedConfig
- type Subjects
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MintAccountJWT ¶
func MintAccountJWT(operatorKP nkeys.KeyPair, accountName string, accountKP, signingKP nkeys.KeyPair) (string, error)
MintAccountJWT signs a fresh Account JWT for accountName, with the supplied signing-key public registered as an authorized signer. The Operator key (`operatorKP`) signs the JWT — caller is responsible for that key's security (production: env var).
JetStream limits are set to unlimited (-1) — a single ppz-server is the only client of every account anyway, so per-account quotas don't add safety, only friction.
func MintUserJWTInAccount ¶
func MintUserJWTInAccount(accountPub string, signingKP nkeys.KeyPair, name string, pubAllow, subAllow []string, expUnix int64) (jwtStr, seed string, err error)
MintUserJWTInAccount mints a User JWT in the supplied account, signed by that account's signing key. Decoupled from `Account` so callers that hold (account_pub, signing_kp) directly — like per-org account loaders in Phase 3.5 — can mint without constructing an Account struct.
expUnix is the JWT's `exp` claim (seconds since epoch). The `nbf` claim is set 30s before now to absorb clock skew.
func StartEmbeddedNATSWithAuth ¶
func StartEmbeddedNATSWithAuth(cfg EmbeddedConfig) (*natsserver.Server, error)
StartEmbeddedNATSWithAuth boots an embedded nats-server with decentralized auth: the supplied operatorJWT is set as the trusted operator, and the account JWTs are preloaded into the in-memory resolver. JetStream + auth requires a system account.
Returns the running server. Caller is responsible for Shutdown().
Types ¶
type Account ¶
type Account struct {
AccountPub string // public nkey of the Account
SigningKey nkeys.KeyPair // derived from PPZ_NATS_ACCOUNT_SIGNING_SEED; held in memory only
AccountJWT string // signed by Operator; loaded into the embedded NATS resolver at boot
}
Account is the runtime state ppz-server needs to mint User JWTs. Loaded once at boot from environment via LoadAccountFromEnv.
func LoadAccountFromEnv ¶
LoadAccountFromEnv reads the Account credentials from process env vars (PPZ_NATS_ACCOUNT_JWT, PPZ_NATS_ACCOUNT_SIGNING_SEED). Fails fast at boot if either is missing or malformed.
func (*Account) MintServerUserJWT ¶
MintServerUserJWT mints a User JWT with broad pub/sub permissions in this Account. ppz-server uses this to authenticate its own in-process NATS connection (which manages JetStream streams across every org) — no cross-org isolation needed.
func (*Account) MintUserJWT ¶
func (a *Account) MintUserJWT(accountID string, expiry time.Duration) (jwtStr, seed string, err error)
MintUserJWT generates a fresh user nkey pair, signs a short-lived User JWT (with the subject scope for accountID embedded), and returns (jwt, seed) for handing to a daemon over /auth/exchange. The seed is the user's own private key — ppz-server keeps no copy.
type EmbeddedConfig ¶
type EmbeddedConfig struct {
Host string // "" → 127.0.0.1
Port int // 0 → OS picks
OperatorJWT string
AccountJWT string
SystemAccountJWT string // optional — required for JetStream
JetStream bool // false → JS disabled (auth-only mode, used by tests)
}
EmbeddedConfig groups the config for StartEmbeddedNATSWithAuth. All JWT fields are required; SystemAccountJWT enables JetStream.
type Subjects ¶
Subjects describes the NATS subject scope embedded in a User JWT.
func SubjectsForOrgUser ¶
SubjectsForOrgUser returns the standard ppz subject scope for a user in accountID. The org_id is the lowercase-hyphenated UUID, used as the subject root (matches natsubj.SubjectFor).
pub: <accountID>.> — own data-plane subjects
_INBOX.> — reply subjects for NATS request/reply
$JS.API.> — JetStream API (stream/consumer mgmt)
sub: <accountID>.> — own data-plane subjects
_INBOX.> — receive replies
Known limitation: $JS.API.> isn't subject-pattern scopable per-org (stream names occupy a single subject token, so wildcards can't constrain the prefix). All orgs share the same JetStream API surface. Data-plane isolation (the load-bearing case for broadcasts) IS enforced via the <accountID>.> scope. Per-org account isolation is the proper fix; tracked in V3+ out-of-scope.