Org IS the role:
- built-in org = full admin access to all compliance modules
- Any other org = self-service only (sessions, applications, kyc)
Removed: RequirePermission, HasRole, X-User-Roles, X-User-IsAdmin
Added: requireOrgAccess (checks X-Org-Id against adminOrg)
Added: localnet environment config
- Strip 9 identity headers at start of every request (prevents injection via direct pod access)
- Remove X-Account-Id fast path and fallback headers from frontend handlers (fixes IDOR)
- Add gRPC auth interceptor with IAM JWKS validation
- Fix RBAC role splitting (comma-separated X-User-Roles)
- Move webhook endpoint outside auth middleware
- Fatal on empty SSN_HMAC_KEY in production
- Remove dead admin store code
- Remove duplicate CORS handler from compliance router
- Fix writeErr JSON injection (use json.Marshal)
- Propagate isAdmin + roles claims from JWT
Middleware now validates Bearer tokens against IAM JWKS endpoint
(IAM_ENDPOINT/.well-known/jwks). Extracts sub, owner, email claims
and sets X-User-Id, X-Org-Id, X-User-Email headers.
Also accepts gateway-propagated headers (X-User-Id) for deployments
behind Hanzo Gateway. One auth path: IAM only.
All authentication now goes through Hanzo Gateway → IAM JWKS.
The broker reads identity from gateway-propagated headers
(X-User-Id, X-Org-Id, X-User-Email, X-User-Roles) and never
validates JWTs or API keys internally.
- Remove auth.Store, auth.APIKey, API key middleware
- Remove admin.Middleware from compliance router
- Remove /auth/login and /auth/verify endpoints
- RBAC reads X-User-Roles header from gateway
- Reviewer identity from X-User-Id header
- /healthz remains unauthenticated
- Rename super_admin → superadmin
Default grant for authenticated IAM users now only covers "read" (browsing
market data). Trade permission requires explicit role assignment to prevent
pre-KYC users from placing orders on the ATS.
resolveUserID now checks X-User-Id first (set by ATS injectAuthHeaders)
before falling back to X-Gateway-User-Id and X-Hanzo-User-Id.
resolveUserAccounts short-circuits on X-Account-Id + optional
X-Account-Provider headers, skipping the ListAccounts call when the
caller already knows the sub-account.
IAM-authenticated users no longer get implicit admin access. RequirePermission
now checks X-User-Roles header for the required permission. Default IAM users
without explicit roles get read + trade only. Admin endpoints require explicit
admin role in JWT claims.
Adds hasRole() helper for comma-separated role string parsing.
resolveConid now uses POST /iserver/secdef/search for proper
multi-exchange symbol resolution. Supports TSX symbols (U.UN,
SPUT.TO), LSE (VOD.L), HKEX, etc. SMART routing auto-selects
best execution venue.
When the caller doesn't provide explicit provider→account mappings,
SmartOrder now auto-discovers the first available account for the
best provider. Fixes "no account configured" errors from ATS.
- SSN_HMAC_KEY: now required unconditionally (init panic) — no fallback
- CORS: localhost origins only allowed when BROKER_ENV != production
- Role mutation (POST/PATCH/DELETE /roles) restricted to super_admin or
owner via requireSuperAdmin guard — prevents self-escalation (HIGH-3)
- Role list/get now requires roles:read RBAC guard
- Add RBAC guards to ALL read endpoints (pipelines, sessions, funds,
esign, templates, dashboard, users, transactions, reports, settings,
credentials, billing, envelope views) — previously 20+ GET routes
had no permission checks
- Wire Jube client into amlHandler via RouterOption pattern
- Replace NewRouter variadic authStore param with proper RouterOption API
- Delegate seed roles to rbac.DefaultRoles() — fixes drift where seed
was missing aml and applications modules
- Delegate modules list to rbac.ComplianceModules() — single source of truth
- Delegate onboarding helpers (newApplicationSteps, isTerminalStatus,
markStepCompleted, markStepFailed) to compliance library
- SSN HMAC key: hard-fail in production if SSN_HMAC_KEY not set
- Add audit logging when super_admin RBAC bypass is used
- Credential management elevated to roles:admin (was unguarded)
Replace local compliance types, store, MemoryStore, and jube package with
imports from github.com/luxfi/compliance. Broker's pkg/compliance/ is now a
thin HTTP adapter layer:
- compliance.go: type aliases re-exporting all types from library
- store.go, jube/: removed (moved to library)
- postgres_store.go: kept (pgx dependency stays in broker)
- All HTTP handlers unchanged (types are aliases, fully compatible)
- Router imports jube from library instead of local subpackage
go.mod adds: require github.com/luxfi/compliance v0.1.0
replace github.com/luxfi/compliance => ../compliance
All 50+ compliance tests pass. No handler code changes needed.
- SSN hashing: upgrade from plain SHA-256 to HMAC-SHA256 with keyed secret
(SSN_HMAC_KEY env var from KMS) to prevent rainbow table attacks on the
small 10^9 SSN keyspace
- Document uploads: validate MIME type (pdf/jpeg/png/tiff only), enforce
10MB size limit, validate base64 encoding
- Error messages: sanitize all handler error responses to prevent leaking
internal store errors (PostgreSQL query details, path info) to clients
- Rate limiting: add per-IP rate limits (10/min) on KYC verify, AML screen,
and AML risk-assessment endpoints
- Security headers: add X-Content-Type-Options, X-Frame-Options, Cache-Control
to all compliance responses
Auth middleware now accepts:
1. X-User-Id + X-Org-Id headers (set by Gateway after JWT validation)
2. API key (Authorization: Bearer or X-API-Key) for service-to-service
No X-IAM-, X-Broker-, or other prefixed headers. One way to auth.
When X-Broker-User-Id header is present, the request comes from the
ATS proxy which already validated the user's JWT via Gateway. Skip
API key check and pass through to allow trading endpoints to work
for authenticated users.
Same issue as snapshots — Alpaca sandbox returns 404 for
/v1beta3/crypto/us/{sym}/bars but works with
/v1beta3/crypto/us/bars?symbols={sym}. Also handle the wrapped
response format {"bars":{"BTC/USD":[...]}} vs stock {"bars":[...]}.
Alpaca sandbox's single crypto snapshot endpoint (/v1beta3/crypto/us/{sym}/snapshot)
returns 404. Use the batch endpoint (/v1beta3/crypto/us/snapshots?symbols={sym})
which works on both sandbox and production.
Move all 16 provider env-var registration into a reusable function.
Any ATS/BD/TA calls envconfig.RegisterFromEnv(registry) instead of
duplicating 130 lines of if-blocks. brokerd/main.go drops from 264
to 88 lines. Tests included.
Remove KYC, application, and onboarding code from broker — now lives
in the standalone luxfi/compliance module. Broker remains a pure
trading router with admin auth, settlement, and multi-provider SOR.
Wire admin user config in main. Update LLM.md.
ATS prefunding pool for instant-buy execution while ACH settles.
Capital reservation with per-user/per-tx/utilization limits.
Margin monitoring with configurable warning/call/liquidation thresholds.
KYC-tiered instant buy limits (basic $250 → institutional $250k).
Full settlement lifecycle: reserve → execute → ACH clear/fail → settle/liquidate.
49 comprehensive tests including concurrency and end-to-end lifecycle.
Password hashing with SHA-256 + salt (never plaintext). JWT tokens
for admin dashboard access with role-based claims. Middleware for
protected route groups. Comprehensive tests for auth flows, token
validation, expiration, and tampering detection.