Files
WRNexusJS/docs/WRNexusJS-Improvement-Plan.md
T
Clintchiz 2c960fc1dc
Quality / quality (ubuntu-latest) (push) Failing after 9m49s
Quality / quality (windows-latest) (push) Canceled after 0s
refactor: migrate legacy wire namespace to wrn
2026-08-12 18:51:15 +05:30

501 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WRNexusJS — Security & Power Improvement Plan
Prepared for WorkRoot · covers `E:\WrNexus` (framework v0.8.4)
This plan is scoped and sequenced the way the repo's own roadmap docs are (`ROADMAP-V1.md`,
`IMPLEMENTATION-ROADMAP-0.8.md`): version-gated phases, one package/file set per item, with
implementation, tests, and doc updates called out per item so each phase can ship as a real release
with a `bun run validate:0.X` gate, like 0.7 and 0.8 did.
Every proposed API below follows conventions already in the repo (workspace `package.json` shape,
`Middleware`/`Context` typing from `@wrnexus/core`, ASVS row format in `docs/SECURITY-ASVS-5.md`,
audit-sink pattern from `packages/authz/src/audit.ts`) rather than inventing new patterns.
---
## Phasing overview
| Phase | Version | Theme | New packages | Est. effort |
| ----- | ------------- | ----------------------------------------------- | ---------------------------------------------- | ----------- |
| 1 | 0.8.5 (patch) | Security default fixes, no breaking changes | none | 12 weeks |
| 2 | 0.9 | Distributed rate limiting + live security audit | `@wrnexus/ratelimit-redis` | 23 weeks |
| 3 | 0.10 | Product-critical DX packages | `@wrnexus/mail`, `@wrnexus/flags` | 46 weeks |
| 4 | 0.11 | Search + AI pairing | `@wrnexus/search` | 34 weeks |
| 5 | 0.12 | Enterprise/government readiness | SAML in `@wrnexus/auth`, `@wrnexus/compliance` | 57 weeks |
| 6 | 1.0 | Monetization + ecosystem | `@wrnexus/billing`, public release strategy | 610 weeks |
Total: roughly 68 months at a small-team pace, phased so each release is independently shippable
and dogfoodable on `workroot.in` / `wrnexusjs.dev` / the WRNexus SaaS itself before the next phase
starts.
---
## Phase 1 — v0.8.5: Security default fixes
No new packages. Pure hardening of existing code, all changes are config-default flips, so they are
non-breaking for anyone who already sets these fields explicitly and only change behavior for people
relying on the current default.
### 1.1 Trusted Types default allowlist
**File:** `packages/core/src/headers.ts`, function `applyTrustedTypesDirectives`
**Problem:** `policyNames` defaults to `["*"]` in production, so any script — including an injected
one — can register a Trusted Types policy. This defeats the XSS mitigation Trusted Types exists for.
**Change:**
```ts
// Before
const policyNames =
typeof trustedTypes === "object" && trustedTypes.policyNames?.length
? trustedTypes.policyNames
: ["*"];
// After
const policyNames =
typeof trustedTypes === "object" && trustedTypes.policyNames?.length
? trustedTypes.policyNames
: ["wrnexus", "default"];
```
Update the `TrustedTypesConfig.policyNames` doc comment to explain the new default and how to opt
back into `["*"]` for apps with third-party extensions that need it.
**Tests:** update `packages/core/test/headers.test.ts` assertions that currently expect `*`.
**Docs:** update the "Production configuration baseline" block in `docs/SECURITY-PERFORMANCE-0.7.md`
and add a migration note to `docs/UPGRADE-0.8.3.md`-style upgrade doc for 0.8.5.
### 1.2 HSTS `preload` default
**File:** `packages/core/src/headers.ts`, function `serializeHsts`
**Problem:** `preload: true` is on by default whenever `mode === "production"`. Preload-list
submission is a long-lived commitment (removal takes months across browsers); defaulting it on for
every production build is a footgun for teams not ready to guarantee HTTPS on every subdomain
permanently.
**Change:**
```ts
function serializeHsts(config: HstsConfig): string {
const parts = [`max-age=${config.maxAge ?? 31536000}`];
if (config.includeSubDomains !== false) parts.push("includeSubDomains");
if (config.preload === true) parts.push("preload"); // was: !== false
return parts.join("; ");
}
```
`includeSubDomains` can stay default-on (safe, reversible); only `preload` flips to opt-in.
**Tests:** update `packages/core/test/headers.test.ts`.
**Docs:** update `SECURITY-ASVS-5.md` row for `v5.0.0-3.4.1` evidence note and the production config
baseline example (explicitly show `hsts: { preload: true }` as something apps opt into, with a
one-line warning comment).
### 1.3 Live security-header verification (`--url` mode)
**File:** `packages/cli/src/security-command.ts`
**Problem:** `securityAudit()` already exists and is solid — it loads the local app config, builds a
synthetic request/response, and runs `withSecurityHeaders` to check what headers _would_ be emitted.
It never checks what a _deployed_ site is actually serving, so a misconfigured reverse proxy,
missing env var, or config drift between local and production is invisible to `wrnexus security
audit` today.
**Change:** add a second code path that takes a URL instead of an app root:
```ts
export interface SecurityAuditOptions {
appRoot?: string;
/** Fetch a live deployment and audit its actual response headers instead of a local config. */
url?: string;
}
export async function securityAudit(options: SecurityAuditOptions): Promise<SecurityAuditReport> {
const headers = options.url
? await fetchLiveHeaders(options.url)
: await securityHeaders(resolve(options.appRoot ?? "."));
// same checks[] logic runs against either header source
...
}
async function fetchLiveHeaders(url: string): Promise<Record<string, string>> {
const res = await fetch(url, { method: "HEAD", redirect: "manual" });
return Object.fromEntries(res.headers.entries());
}
```
CLI surface: `wrnexus security audit --url=https://workroot.in` — same `SecurityAuditCheck[]` table
output as the local mode, so it's a drop-in mental model for anyone who's already used the local
version.
**Tests:** `packages/cli/test/security-command.test.ts` — mock `fetch`, assert the same check IDs run
against a header map built from a fake `Response`.
**Docs:** update `SECURITY-ASVS-5.md`'s intro line ("Run `bun run security:asvs` ... `wrnexus
security audit`") to mention the `--url` mode explicitly.
---
## Phase 2 — v0.9: Distributed rate limiting
### 2.1 `@wrnexus/ratelimit-redis`
**Problem:** `packages/core/src/ratelimit.ts` is honestly documented as process-local by default,
with a clean `RateLimitStore` interface for swapping in a shared store — but no first-party
implementation ships. Every team running more than one instance has to write their own Redis
`INCR`/`PEXPIRE` bucket store before rate limiting actually works in production.
**New package layout** (mirrors `packages/captcha`'s `stores/redis.ts` pattern, which already
exists for CAPTCHA — this is literally copying a pattern you've already built once):
```
packages/ratelimit-redis/
package.json
src/
index.ts # createRedisRateLimitStore()
client.ts # thin ioredis/bun-redis wrapper, injectable client
test/
store.test.ts # against a real or mocked Redis
README.md
```
**API:**
```ts
import { createRedisRateLimitStore } from "@wrnexus/ratelimit-redis";
import { rateLimit } from "@wrnexus/core";
const store = createRedisRateLimitStore({ url: process.env.REDIS_URL! });
app.use(rateLimit({ store, max: 100, windowMs: 60_000 }));
```
Implementation: one atomic Lua script (`INCR` + conditional `PEXPIRE`) to avoid a race between the
increment and the expiry set — same correctness bar as the in-memory store's atomicity within a
single process.
**package.json:**
```json
{
"name": "@wrnexus/ratelimit-redis",
"version": "0.9.0",
"type": "module",
"main": "./src/index.ts",
"exports": { ".": "./src/index.ts" },
"dependencies": { "@wrnexus/core": "workspace:*" }
}
```
**Tests:** `store.test.ts` covering window rollover, concurrent-hit correctness (fire N parallel
`hit()` calls, assert exact count), and store failure fallback behavior (Redis down → fail open with
a warning log, documented explicitly so nobody is surprised).
**Docs:** add a row to `SECURITY-SUPPORT-MATRIX.md` under a new "Rate limiting" area, and link it from
the `RateLimitStore` doc comment in `packages/core/src/ratelimit.ts`.
---
## Phase 3 — v0.10: Product-critical DX packages
### 3.1 `@wrnexus/mail`
**Problem:** there's no first-party way to actually send email. The queue example
(`app/queues/welcome-email.ts`) shows _scheduling_ an email job but nothing implements delivery.
**Package layout:**
```
packages/mail/
package.json
src/
index.ts
send.ts # sendMail(), core envelope type
providers/
resend.ts
ses.ts
postmark.ts
smtp.ts
dev-inbox.ts # captures mail in dev instead of sending; wrnexus dev shows it in DevToolbar
components/ # optional .wrn email-template partials, reusing the compiler
test/
send.test.ts
dev-inbox.test.ts
README.md
SECURITY.md
```
**API (mirrors the `SafeUrlPolicy`/provider-adapter shape from `@wrnexus/captcha`'s providers):**
```ts
export interface MailProvider {
send(message: MailMessage): Promise<MailResult>;
}
export interface MailMessage {
to: string | string[];
from: string;
subject: string;
html?: string;
text?: string;
replyTo?: string;
headers?: Record<string, string>;
}
export function createMailer(provider: MailProvider): { send(m: MailMessage): Promise<MailResult> };
// providers/resend.ts
export function resendProvider(opts: { apiKey: string }): MailProvider;
```
**Dev-mode behavior:** when `mode !== "production"`, `createMailer` wraps any provider with
`devInboxProvider()`, which stores messages in memory and surfaces them in the DevToolbar (new panel,
same pattern as the existing SQL/queue/realtime DevToolbar providers listed in
`SECURITY-PERFORMANCE-0.7.md` §18) instead of actually sending — this is the single highest-value DX
win in this package, since it removes the "did my email actually work" debugging loop entirely.
**Security notes for `SECURITY-SUPPORT-MATRIX.md`:** framework guarantees provider-secret handling
stays server-side and never serializes into hydration payloads (reuse the existing serialization
redaction from `@wrnexus/security/serialization.ts`); app/operator responsibility covers SPF/DKIM/DMARC
DNS records and provider account reputation.
**Effort:** ~23 weeks for `resend` + `smtp` providers, dev inbox, and DevToolbar panel; `ses` and
`postmark` can follow as a fast-follow since they share the same `MailProvider` interface.
### 3.2 `@wrnexus/flags`
**Problem:** no feature-flag primitive. Every team building past MVP eventually needs gradual
rollout, and right now they'd bolt on a third-party SDK with no integration into WRNexusJS's
`.wrn` reactivity or SSR model.
**API:**
```ts
export interface FlagsStore {
get(key: string, ctx: FlagContext): Promise<boolean | string | number>;
}
export function createFlags(store: FlagsStore): {
isEnabled(key: string, ctx: FlagContext): Promise<boolean>;
variant(key: string, ctx: FlagContext): Promise<string | undefined>;
};
// stores/memory.ts, stores/db.ts (reuses @wrnexus/db), stores/percentage.ts (deterministic hash rollout)
```
`.wrn` integration: expose `load server { const enabled = await flags.isEnabled("new-dashboard",
ctx) }` so flags flow into `props`/`state` the same way `load server` results already do — no new
compiler syntax needed, just a helper package.
**Effort:** ~12 weeks; the DB-backed store reuses `@wrnexus/db` migration patterns already in the
repo, so most of the work is the percentage-rollout hashing and the DevToolbar panel.
---
## Phase 4 — v0.11: Search, paired with `@wrnexus/ai`
### 4.1 `@wrnexus/search`
**Problem:** no first-party search story, despite already having `@wrnexus/ai` for embeddings/LLM
calls — search and RAG are the two things most SaaS apps need `@wrnexus/ai` _for_, so this is the
natural pairing package.
**Package layout:**
```
packages/search/
src/
index.ts
adapters/
postgres-fts.ts # tsvector + GIN index helpers, generated migration
sqlite-fts5.ts
pgvector.ts # embeddings via @wrnexus/ai, cosine-distance query helper
typesense.ts # optional hosted adapter
test/
```
**API:**
```ts
export function createSearchIndex(adapter: SearchAdapter, table: string, columns: string[]);
const results = await search.query("customer onboarding", { limit: 10 });
// pairs directly with @wrnexus/ai
import { embed } from "@wrnexus/ai";
const vector = await embed(text);
await search.upsertVector(id, vector);
```
**Effort:** ~34 weeks — Postgres FTS and SQLite FTS5 adapters first (no new infra dependency,
reuses `@wrnexus/db`'s existing driver abstraction), pgvector and Typesense as fast-follows.
---
## Phase 5 — v0.12: Enterprise & government readiness
This phase is prioritized specifically because `workroot.in` markets to "enterprises & governments" —
these two items are the actual procurement blockers for that buyer, more than any generic framework
feature would be.
### 5.1 SAML support in `@wrnexus/auth`
**Problem:** OAuth/OIDC is implemented (per `SECURITY-SUPPORT-MATRIX.md`), but large-enterprise and
government IT departments frequently mandate SAML 2.0 specifically for SSO procurement, regardless of
OIDC's technical merits.
**Location:** `packages/auth/src/saml/` — new subdirectory alongside the existing OAuth/OIDC code,
same `AuthProvider` interface shape so it plugs into the existing account/session engine
(`packages/auth/src/engine.ts`) without a parallel auth system.
**Scope:** SP-initiated SSO, signed assertion validation, configurable IdP metadata (Okta, Azure AD,
Google Workspace, ADFS as the four IdPs to certify against first — that covers the large majority of
enterprise/government IT estates).
**Security notes:** reuse the existing `AUTH_SECURITY_EVENT_TYPES` vocabulary for SAML-specific
events (assertion replay attempt, signature validation failure, clock-skew rejection) so they flow
into the same audit pipeline as every other auth event — no parallel logging system.
**Effort:** ~45 weeks; SAML assertion validation is fiddly (XML canonicalization, signature
wrapping attacks) and deserves a dedicated security review pass before release, not just unit tests.
### 5.2 `@wrnexus/compliance`
**Problem:** `packages/authz/src/audit.ts` already has a well-built `AuthzAuditSink` interface with
log-injection-safe formatting (`logSafe()`) and memory/console sinks — but no durable, exportable
store. For SOC 2-style evidence or India's DPDP Act data-processing records, teams need retained,
queryable, exportable audit trails, not console lines.
**Package layout:**
```
packages/compliance/
src/
index.ts
sinks/
db.ts # durable AuthzAuditSink + auth security-event sink, reusing @wrnexus/db
export.ts # CSV/JSON export with retention-window filtering
retention.ts # configurable retention policy + scheduled purge
test/
```
**API:**
```ts
import { dbAuditSink } from "@wrnexus/compliance";
authz.configure({ auditSink: dbAuditSink({ retentionDays: 365 }) });
const report = await compliance.exportAuditTrail({ from, to, format: "csv" });
```
This is the package I'd actually build _first_ internally for the WRNexus SaaS itself, since any
enterprise/government customer of WRNexus will ask WorkRoot for exactly this evidence during their
own procurement review — dogfooding it validates the design before it ships to other developers.
**Effort:** ~2 weeks on top of the existing audit-sink groundwork, since most of the hard part
(safe event formatting, sink interface) is already done.
---
## Phase 6 — v1.0: Monetization and ecosystem
### 6.1 `@wrnexus/billing`
**Problem:** no payments package, despite WRNexus itself being a billed SaaS product — this is the
package where dogfooding value is highest.
**API:**
```ts
export interface BillingProvider {
createCheckoutSession(params): Promise<{ url: string }>;
verifyWebhook(req: Request): Promise<BillingEvent>;
getSubscription(customerId: string): Promise<Subscription>;
}
// providers/stripe.ts, providers/razorpay.ts (India-relevant)
```
Webhook verification reuses `@wrnexus/security`'s constant-time comparison helpers (same primitive
already used in `packages/core/src/csrf.ts`'s `timingSafeEqual`) for signature checks. Usage metering
hooks into `@wrnexus/observability`'s existing counters/gauges rather than a new metrics system.
**Effort:** ~56 weeks for Stripe + Razorpay, subscription lifecycle, and webhook handling with
proper idempotency-key handling (a real source of billing bugs if skipped).
### 6.2 Public release strategy — open-core
**Problem:** `@wrnexus/*` is currently private, so nobody outside WorkRoot can `bun install` any of
it. This is the actual ceiling on "developer power," not any single missing feature.
**Recommended split:**
| Tier | Packages | License |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| Open (public npm) | `core`, `ssr`, `compiler`, `syntax`, `router`, `store`, `reactive`, `security`, `ui`, `cli`, `dev-server`, `dev-toolbar` | MIT or Apache-2.0 |
| Paid/enterprise | `billing`, `compliance`, SAML in `auth`, `authz` advanced policy engine | Commercial license, distributed via the private registry you already run |
**Rollout steps:**
1. Audit the `open` tier packages for any WorkRoot-specific secrets/config baked in (`scripts/`
already has `generate-sbom.mjs` and `check-public-api.mjs` — extend `check-public-api.mjs` to also
flag internal-only references before a package is promoted to the public tier).
2. Publish under the `@wrnexus` npm org with the existing `PUBLISHING.md` process, starting with
`core` + `cli` (the minimum to `bunx @wrnexus/cli create my-app` publicly).
3. Public GitHub repo for the open tier only (mirrored from the monorepo via the existing
`stage:packages` script's publish pipeline, not a manual copy).
4. Keep `docs/ROADMAP.md`-style public roadmap visible so early external adopters see what's coming.
**Effort:** ~46 weeks of packaging/licensing/CI work, separate from any new feature work above — this
can run in parallel with Phase 5.
### 6.3 Lean into `@wrnexus/ai` + `@wrnexus/mcp`
**Problem:** these packages already exist and are ahead of most frameworks, but aren't positioned as
a headline feature anywhere in the marketing (`wrnexusjs.dev` homepage doesn't mention AI/MCP at all
per the live screenshot taken earlier).
**Recommendation:** ship an official MCP server (`packages/mcp/src/index.ts` already has a `stdio.ts`
transport — check whether it currently exposes framework introspection, e.g. route listing, `.wrn`
component schema, or `wrnexus doctor` output as MCP tools) so agentic coding tools (Claude Code,
Cursor, etc.) can scaffold and modify WRNexusJS apps with structured tool calls instead of guessing at
the `.wrn` syntax from grepped examples. This is a low-cost, high-differentiation move given how much
of the ecosystem is moving toward agent-built apps.
**Effort:** ~23 weeks to wrap existing CLI commands (`inspect`, `doctor`, `routes`, `generate`) as
MCP tools, since the underlying logic already exists in `packages/cli/src`.
---
## Cross-cutting requirements for every phase
- Every new/changed package needs a `SECURITY-SUPPORT-MATRIX.md` row (framework guarantee vs.
app/operator responsibility) before release, matching the existing table format.
- Every new package needs an ASVS evidence row in `SECURITY-ASVS-5.md` if it touches auth, secrets,
network requests, or user input — same two-column (implementation evidence / verification evidence)
format already used.
- `bun run validate:0.X` release gate (`scripts/validate-0.8.mjs`-style) should get a new
`validate-0.9.mjs` etc. per phase, following the existing per-minor-version validation script
pattern rather than one growing script.
- `CHANGELOG.md` entries per release, same format as the 0.8.3/0.8.0 entries already there.
- Each phase should ship an example page/route in `examples/basic-app` (mirroring
`auth-showcase`, `captcha-showcase`, `i18n-showcase`) — e.g. `examples/mail-showcase`,
`examples/billing-showcase` — so the roadmap's own "executable examples" discipline
(`ROADMAP-COMPLETION-REPORT.md`) continues.
---
## Suggested sequencing rationale
Phase 1 ships first because it's the only phase with zero new surface area — pure default fixes that
improve every existing deployment (including `workroot.in` and `wrnexusjs.dev` themselves) without
anyone changing their code. Phases 24 build developer-facing power in the order teams actually hit
the wall (rate limiting under real traffic → needing email → needing flags → needing search once an
app has enough data to search). Phase 5 is prioritized ahead of Phase 6 despite being harder, because
it directly unblocks revenue-relevant deals in WorkRoot's actual pipeline (enterprise/government
procurement). Phase 6's public release is last on purpose — it should launch once there's a stronger
package lineup behind it, so the first public impression of `@wrnexus/*` is "batteries-included," not
"promising but thin."