Framework packages move in lockstep, so "supported version" is one number covering all 31 — the 30 @ultimat3/* packages and the unscoped create-ultimate. Re-derive the list with bun run scripts/release-workflow.ts --json.
| Version | Supported |
|---|---|
the latest major — npm view @ultimat3/core version |
✅ security fixes |
| every earlier major | ❌ upgrade — see Upgrading |
Only the latest major gets fixes. A fix ships as a patch to every package in one release, on the same lockstep rule as any other release; there are no per-package security branches and no backports. Report against the latest major the table above names.
Report privately via GitHub Security Advisories. Do not open a public issue for a vulnerability.
Include: the affected package and version, a reproduction, the impact, and any suggested fix. We acknowledge within 3 business days.
Design decisions that carry security weight, so you know what to audit:
| Area | Posture |
|---|---|
| Authz | One policy definition enforced across HTTP, live queries, jobs, and MCP. There is deliberately no second authz system. An action without a policy fails at registration. |
| MCP tool visibility | Three outcomes, never blurred. A tool the caller's role may not invoke is omitted from tools/list and answers ToolNotFound — never Forbidden, so there is no enumeration oracle. A tool the connection's token lacks the scope for is refused explicitly (X_MCP_SCOPE_DENIED), because a well-behaved client can fix that. A tool that ran and whose policy denied the input answers X_FORBIDDEN, identical to the HTTP answer. Visibility is fail-closed and input-independent: visibleTo is either a role allowlist — which admits only the roles it names, so a caller with no matching role is refused — or a predicate over the caller alone, which structurally cannot read call arguments, so existence cannot be probed by varying them. Computed per connection, and every outcome is audited, ToolNotFound at warn. |
| MCP dev server | db.query is read-only in four layers: a SELECT-only Postgres role, BEGIN READ ONLY + SET LOCAL statement_timeout, a single-read parse (batches, statement-level write keywords including data-modifying CTEs, locking clauses, EXPLAIN ANALYZE and whole function families — file access, pg_advisory_* locks, set_config, pg_sleep* — are all refused, by prefix of the called function name — quoted and schema-qualified spellings included — so a spelling nobody listed is refused rather than admitted), and caps — limit defaults to 100 rows and clamps to a hard maximum of 1000, plus a 256 KiB byte cap. The role layer is conditional: a managed Postgres that refuses CREATE ROLE/GRANT leaves it out, and the response's guards array names the layers that actually engaged. db.migrate refuses any database that is not a branch DB. The /_x dashboard refuses to mount in production. |
| Multi-tenancy | A tenant-scoped entity queried without an org predicate throws X_TENANCY_UNSCOPED. It is a runtime guard, not a convention. |
| Secrets | Env vars declared secret are redacted in logs, traces, and error output. .env is gitignored; .env.example documents the shape with no values. |
| Admin | Every admin mutation is appended to an audit log with actor, before/after diff, and request id. Destructive actions require re-confirmation. |
| Preview deploys | Any environment but production serves Disallow: / in robots.txt and no sitemap (isIndexable in @ultimat3/seo), Service-worker caches are namespaced by build ID (cacheNamespace in @ultimat3/pwa), so a preview isolates its cache from production only on a different build ID or a different origin. |
| CSP | Locked defaults. The theme inline script ships with its sha256 hash so no unsafe-inline is needed. |
| Egress in tests | Sealed by default — sealNetwork() replaces global fetch, and any unmocked outbound request throws X_TEST_NETWORK_SEALED naming the URL and the line that allows it. Opting out is an env var (ULTIMATE_TEST_ALLOW_NET=1), never an API, so no test file can quietly unseal the network for itself. One documented exception: a caller that swallows the refusal defeats the seal, and @ultimat3/scraping's /robots.txt read is one — robotsFetcher's catch { return undefined } turns "sealed" into "unreadable", which the robots gate reads as allow-everything, so an offline fakeBrowser/fixtureBrowser scrape attempts one real request per origin and the suite is green either way. Verified 2026-08-19; tracked in wiki/Known-Gaps.md. |
As of 2026-10. None of these is closed by the current release; audit accordingly. The full defect list, security-weighted or not, is Known gaps.
-
No third-party security audit. The auth stack has had no external review. Better Auth binds through
AuthAdapterrather than being a dependency, so the seam is narrow and swappable — but neither the built-in adapter nor a Better Auth binding has been audited by anyone outside this repo. -
Rate limiting holds across replicas only on the Postgres stores.
postgresRateLimitStore(@ultimat3/http, tableSQL_RATE_LIMIT_TABLE) andpostgresAuthLimiter(@ultimat3/auth) count every replica against one bucket;memoryRateLimitStore()andMemoryAuthLimitercount per process, so N replicas on them enforce N × the configured bucket. The store and the app each declare ascope('process' | 'shared'),httpServer({ rateLimitStore })reaches the seam through the supported API, and an app declaringscope: 'shared'against a per-process store refuses at boot —X_RATE_LIMIT_NOT_SHARED, andX_AUTH_LIMITER_NOT_SHAREDfor the credential path.The two sides answer a missing
scopedifferently, and the HTTP one refuses the boot. This file said "both default to'process'" and that is false for HTTP:Side No scopedeclaredHTTP — resolveRateLimitConfigthrows X_RATE_LIMIT_SCOPE_UNSETwhile the limiter is enabled.DEFAULT_RATE_LIMITis typedOmit<RateLimitConfig, 'scope'>, so it structurally cannot carry one. Defaulting made "we did not ask" indistinguishable from "the app said one replica", and the chart'sreplicas: 3then enforced every number three times over, silently.enabled: falsereads as'process'— a limiter that is switched off has nothing to be wrong aboutCredentials — DEFAULT_AUTH_RATE_LIMITdefaults to 'process'. Per-IP and per-account lockout, one process' worth of stateA shared proxy can add a fleet-wide limit at ingress, but it does not satisfy the app's own guards:
scope: 'shared'still needs the Postgres stores, and an enabledscope: 'process'limit still counts per replica. -
One master key seals every purpose — accepted,
As of 2026-10-07(owner decision 16, #648).seal()encrypts sealed entity columns, MFA secrets, OAuth handshakes and MCP confirmation arguments under the keyx secretsmanages (ULTIMATE_SECRETS_KEY, else.secrets.key); only the IV's MAC key is derived from it under a fixed label (packages/core/src/seal-keys.ts). There is no subkey per purpose, so one leaked key opens every sealed value, and rotating it (x secrets rotate, the retired ringULTIMATE_SECRETS_RETIRED_KEYS) rotates all of them together. Treat the key as the most sensitive secret the app holds. -
A tenant has its own login lockout — kept,
As of 2026-10-07(owner decision 17, #648). Besides the per-IP and per-account buckets, failed sign-ins count against the org:rateLimit.orgMaxAttempts, defaultmaxAttempts × 20= 100 per 15-minute window (packages/auth/src/rate-limit.ts,ORG_ATTEMPT_FACTOR). An attacker who knows about 20 of an org's addresses can use up that allowance and lock the whole org out of password sign-in forlockoutMs. It stays because it is what stops a spray spread across one tenant's accounts. RaiseorgMaxAttemptsfor a large tenant; release a held org withawait auth.orgLimiter.recordSuccess(orgKey(orgId))(the fixX_ACCOUNT_LOCKEDprints for a held org). -
The sync protocol has had no adversarial review. Tiers 1–2 ship, and so does tier 3's client half: record types marked
persist: trueare stored under principal-scoped IndexedDB keys and restored on page boot before the first socket connection, stale until the server confirms them (packages/realtime/src/record-persister.ts); a restore whose principal changed while it read is discarded. Only synced truth is written, never an optimistic overlay — but what a hostile client can send the sync node has not been reviewed by anyone outside this repository.