17 Commits
Author SHA1 Message Date
zeekay dee0d76af8 fix: superuser token fallback in external-only auth mode + scrub PB references
- loadAuthToken now falls back to local validation for _superusers tokens
  when JWKS validation fails in external-only mode. Non-superuser local
  tokens are still rejected. Fixes seeding with IAM enabled.
- Scrub PocketBase references from docs, SDK, and UI config
2026-03-02 15:28:53 -07:00
zeekay e575b49392 fix(platform): skip external auth when IAM_ENDPOINT is empty
When IAM_ENDPOINT is empty or set to 'disabled'/'none', skip JWKS
registration and ExternalAuthOnly enforcement. This allows PB superuser
tokens to work for all collections, enabling data seeding and migration
without requiring a running IAM instance.
2026-03-02 13:59:36 -07:00
zeekay a28b3db6cc fix: vendor-neutral branding — remove all hardcoded "Hanzo Base"
Replace "Hanzo Base" fallbacks with generic "Base" in all Svelte UI
components. When $appName is set (e.g. "<tenant>"), it displays
that instead. Help text uses "the application" rather than a brand.

Files: App.svelte, FullPage.svelte, PageWrapper.svelte, index.html,
PageBackups, PageStorage, PageMail, TrustedProxyAccordion,
CollectionRulesTab, LarkOptions, PageExportCollections
2026-03-01 17:20:08 -07:00
zeekay 917cc4a634 fix: db pool config struct, tests + benchmarks
- DBPoolConfig with tunable ReadConns, ReadIdleConns, MaxPools
- Fixed openPool to use filepath.Dir (was buggy string slicing)
- 6 tests: get/release, LRU eviction, no-evict-in-use, concurrent
  access (100 goroutines), close, data persistence across reopen
- 5 benchmarks: cache hit (22ns), miss (79us), concurrent read
  (6.4us), read/write mix (17us), 500-org eviction pressure (5.9us)
- PoolStats for monitoring (hits, misses, evictions, opens, errors)
2026-03-02 12:42:15 -07:00
zeekay 425d008f4b feat: per-org/per-user SQLite pool manager + request middleware
DBPoolManager: LRU cache of dual-pool SQLite connections (read pool
with 20 conns + write pool with 1 conn per database file). Evicts
least-recently-used pools when capacity is reached.

platformOrgDBResolver middleware (priority loadAuthToken+4):
- Reads X-Org-Id and X-User-Id headers (set by upstream middleware)
- Resolves or auto-provisions the org/user SQLite file
- Stores DBPool in request context via re.Set("orgDB"/"userDB")
- Services are fully stateless — any instance serves any org/user

File layout:
  {DataDir}/orgs/{orgId}/org.db
  {DataDir}/orgs/{orgId}/users/{userId}/data.db

Activated when OrgIsolation="sqlite" in PlatformConfig.
2026-03-02 11:38:31 -07:00
zeekay c1794c3140 feat: enforce pk- read-only, sk- full access at middleware level
Publishable keys (pk-) are restricted to GET/HEAD/OPTIONS.
Any write method (POST/PUT/DELETE) returns 403 with clear error.

Secret keys (sk-), IAM keys (hk-), and JWTs have no method restriction.

Enforcement runs at priority +3 (after identity resolution, before handlers).
Every Base app gets this for free — zero per-route code needed.
2026-03-01 23:23:23 -07:00
zeekay 6a53ac0c9f feat: native IAM API key auth in platform plugin
Every Base app now supports IAM-managed API keys (hk-/pk-/sk-) natively.
Keys are created and managed in IAM — Base resolves them via IAM's
GET /api/get-user?accessKey= endpoint (cached 5min).

Auth chain: JWKS JWT → IAM API key → unauthenticated
Key types:
  pk-/pk_ = publishable (frontend-safe, read-only)
  sk-/sk_ = secret (backend-only, full access)
  hk-    = IAM key (legacy, full access)

Gateway validates JWTs statelessly via JWKS.
Base validates API keys via IAM (one canonical key store).
One way to do auth. IAM is the source of truth.
2026-03-01 23:18:56 -07:00
zeekay 9cb370fe1e refactor(auth): standardize OIDC/JWKS abstraction, remove IAM branding from core
Rename all IAM-specific identifiers to generic OIDC terminology:
- StoreKeyIAMJWKSURL → StoreKeyJWKSURL
- StoreKeyIAMOnly → StoreKeyExternalAuthOnly
- resolveIAMToken → resolveJWKSToken
- iamSub/iamOwner/iamEmail → authSub/authOwner/authEmail
- iamSubToRecordID → subToRecordID

Add externalAuthGuard() middleware to block built-in auth endpoints
(password, OTP, OAuth2, email-change, etc.) when external-only mode
is active. _superusers collection exempt for admin panel login.

Backward compat aliases preserved for StoreKeyIAM* constants.
2026-03-02 15:39:13 -07:00
zeekay fb0dce8533 fix: truncate IAM sub to 15 chars for Base record ID limit 2026-03-02 16:27:13 -07:00
zeekay 724c31293d feat: IAM JWKS fallback in loadAuthToken — Base accepts IAM JWTs natively 2026-03-02 14:40:08 -07:00
zeekay 00609ecedb feat(platform): multi-org service with KMS creds, customer identity, and API routes
- OrgService: per-org config cache, KMS credential resolution with env fallback,
  customer identity provisioning with sequential IDs
- org_configs + org_customers system collections auto-created on boot
- REST routes under /api/platform/org with IAM-based access control
- Remove tenant-authorization header forwarding (unused)
- Simplify auth proxy to forward only Authorization header
2026-03-02 00:37:26 -07:00
zeekay c32ffbab9d feat: per-org + per-user S3 storage isolation with SSE-C encryption
TenantStorage manages isolated S3 prefixes on Hanzo S3 (s3.hanzo.space):

  tenants/{orgSlug}/org/                   ← org-level shared storage
  tenants/{orgSlug}/users/{userId}/        ← per-user isolated storage

Each level gets a unique SSE-C encryption key:
  org key  = HMAC-SHA256(master, "s3:org:" + orgSlug)
  user key = HMAC-SHA256(master, "s3:user:" + orgSlug + ":" + userId)

IAM bucket policies enforce path isolation:
  - Org admins: access tenants/{orgSlug}/*
  - Users: access only tenants/{orgSlug}/users/{userId}/*
  - Zero cross-org or cross-user access

PlatformConfig additions:
  TenantStorageEndpoint — S3 endpoint (s3.hanzo.space)
  TenantStorageBucket — root bucket name
2026-03-02 13:03:58 -07:00
zeekay c3b2d85a07 feat: per-tenant SQLite isolation mode for platform plugin
TenantIsolation config option:
  "prefix"   — (default) shared DB with t_{slug}_ collection prefixes
  "sqlite"   — separate encrypted SQLite per tenant in DataDir/tenants/
  "cloudsql" — separate PostgreSQL per tenant (existing cloudsql plugin)

TenantDB manager:
  - Provision(slug) → creates {DataDir}/tenants/{slug}/data.db
  - DEK(slug) → HMAC-SHA256(masterKey, slug) per-tenant encryption key
  - GetDBPath/Delete/List for lifecycle management
  - Each tenant's SQLite file is independently encryptable, backupable
  - Zero data commingling — physical file isolation

For <tenant>: TenantIsolation="sqlite" gives each customer their own
encrypted database. PII physically separated. FINRA-compliant isolation.
2026-03-02 12:19:30 -07:00
zeekay c909b81fde feat: IAM auth proxy + compliance config in platform plugin
Add generic IAM auth proxy routes (/api/platform/auth/*) for phone
verification, OTP login, signup. Add IAMOrg/IAMApp config fields.
2026-03-02 09:56:27 -07:00
zeekay 5817640d71 feat: add compliance extension to platform plugin
Optional luxfi/compliance integration for KYC/AML, sanctions screening,
and payment validation. Enabled by setting ComplianceEndpoint in
PlatformConfig. Adds 5 API routes under /api/platform/compliance/*.
No new Go dependencies — uses HTTP client to call compliance service.
2026-03-02 02:28:28 -07:00
zeekay a4cb1bc7ea Fix bugs from CTO audit: presence race, IAM cache, KMS token caching, SDK fixes
- presence.go: Replace slice-index callback tracking with ID-based map
  to prevent stale index corruption on unsubscribe
- ws.go: Remove 7 unused WebSocket close code constants
- platform.go: Move config defaults before IAMClient creation, use
  cached p.iam.ValidateToken() instead of standalone function
- kms.go: Add KMS auth token caching with expiry-aware refresh
- collection.ts: Implement unsubscribe() (was silent no-op)
- index.ts: Remove duplicate ListResult type export
- use-query.ts: Fix missing deps bug by serializing full options as
  stable key via JSON.stringify, remove eslint-disable
2026-02-27 10:13:00 -08:00
zeekay 7f17d4b316 Add realtime DX platform: CRDT engine, WebSocket, SDKs, typegen, scheduler, multi-tenant
7-phase implementation of Convex-inspired realtime developer experience:

- crdt/: Go CRDT engine (GCounter, PNCounter, LWWRegister, ORSet, RGA text)
  with state-vector sync protocol and convergence-tested types (31 tests)
- apis/ws.go: Pure RFC 6455 WebSocket endpoint with JWT auth, CRDT sync,
  presence tracking, channel subscriptions
- apis/presence.go: Channel-based presence manager for collaborative awareness
- cmd/typegen/: CLI tool to generate TypeScript interfaces from Base schemas
  (4 tests, all 14 field types mapped)
- plugins/scheduler/: Scheduled functions with exactly-once semantics,
  retry with backoff, JSVM bindings (8 tests)
- plugins/platform/: Multi-tenant platform with IAM (hanzo.id) auth,
  KMS (kms.hanzo.ai) secrets, tenant isolation via collection prefixes,
  RBAC roles, API key management (7 tests)
- sdk/base-js/: @hanzoai/base TypeScript SDK with reactive queries,
  query deduplication, optimistic updates, state versioning, CRDT client
  (72 smoke tests passing)
- sdk/base-react/: @hanzoai/base-react React hooks (useQuery, useMutation,
  useAuth, useRealtime, useCRDT, usePresence) with useSyncExternalStore
2026-02-27 10:00:24 -08:00