Skip to content

AUTH SECURITY

github-actions[bot] edited this page Jul 22, 2026 · 5 revisions

Security and Encryption

Second page of the Authentication section. It covers the current state of the Diffie-Hellman contract, the per-environment security policy, and the configuration values you must never ship with defaults. Read AUTH-HANDSHAKE first for the states these rules apply in.

Diffie-Hellman: current status

The wire protocol defines an in-band Diffie-Hellman negotiation — RSA-signed prime and generator, an encrypted public key exchange — and networking/crypto/diffie defines exactly those contracts:

// Provider prepares protocol Diffie-Hellman values.
type Provider interface {
	// Begin returns encrypted prime and generator values.
	Begin(context.Context) (Parameters, error)
	// Complete consumes a client public key and returns server completion values.
	Complete(context.Context, PublicKey) (Result, error)
}

What exists today is the contract and the packet decoding, not the negotiation. A client that sends Diffie init or complete is disconnected with a clear protocol error (diffie unavailable) rather than strung along with a half-working exchange:

// DiffieInit handles Diffie start packets.
func DiffieInit(context netconn.Context, packet codec.Packet) error {
	if _, err := indiffieinit.Decode(packet); err != nil {
		return err
	}
	return disconnectMissingDiffie(context)
}

Nitro clients handle this fine in development because they can be configured to skip the in-protocol handshake entirely and go straight to the SSO ticket.

The security policy

Enforcement doesn't depend on Diffie being implemented. Each session carries a security policy derived from the environment at connection time:

func SecurityPolicyForEnvironment(environment string) SecurityPolicy {
	if strings.EqualFold(environment, "production") {
		return SecurityPolicy{Mode: SecurityRequired}
	}
	return DefaultSecurityPolicy() // SecurityOptional
}
  • Under SecurityOptional (any non-production PIXELS_ENV), a plaintext connection may authenticate directly.
  • Under SecurityRequired (PIXELS_ENV=production), ValidateAuthenticationSecurity refuses the SSO ticket unless a secure channel is active on the session, and refusing means a typed disconnect, not a silent failure.

The session API supports attaching a secure channel from the transport layer (ActivateSecurity), and once one is ready, all bytes pass through its Open/Seal transparently. Handlers never see any of this: the session unwraps security before dispatch, so packet handlers are byte-for-byte identical on plain and secured connections. That separation is a hard architectural rule, not an implementation detail.

For a real deployment, terminate TLS in front of the server (wss:// at your reverse proxy). That encrypts the transport regardless of the in-protocol handshake's status, and it's the assumption the production posture is built around.

Configuration you must not leave at defaults

Everything boots with defaults for development convenience, and three of those defaults are published in this repository — which makes them exactly as secret as a sticky note on the monitor:

Variable Default Why you must change it
PIXELS_ACCESS_KEY pixels-dev-key Guards every private HTTP route, including SSO ticket creation. Anyone holding it can mint a login ticket for any player.
SSO_KEY pixels-development-sso-key-change-me HMAC key deriving the Redis storage keys for tickets.
PIXELS_ENV development Leaves connection security optional and exposes /docs. Set production.

Related knobs with sane defaults you may still want to tune: SSO_DEFAULT_TTL (five minutes; tickets are consumed within seconds in a healthy flow, so shorter is fine), SSO_PREFIX (Redis namespacing when sharing an instance), and the PIXELS_WS_* family — outbound queue size, read/write timeouts, ping cadence — that governs the WebSocket layer everything above rides on.

Clone this wiki locally