@jimhoyd/urlcode 0.4.0-alpha.2 → 0.4.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/urlcode-authoring/SKILL.md +17 -19
- package/.claude/skills/urlcode-operations/SKILL.md +9 -9
- package/.claude-plugin/marketplace.json +1 -1
- package/CONTRIBUTING.md +20 -16
- package/README.md +44 -54
- package/ROADMAP.md +61 -376
- package/dist/BUILD-MANIFEST.json +15 -15
- package/dist/agents-guide.js +6 -6
- package/dist/build-static.js +2 -0
- package/dist/cli.js +3 -3
- package/dist/explain-cli.js +4 -2
- package/dist/explain.js +7 -1
- package/dist/extensions.js +1 -1
- package/dist/manifest.js +7 -1
- package/dist/mcp-authoring.js +2 -2
- package/dist/mcp.js +1 -1
- package/dist/policies.js +3 -1
- package/dist/prerender.js +4 -0
- package/dist/readiness.js +3 -0
- package/dist/route-diff.js +12 -5
- package/dist/types/explain.d.ts +6 -0
- package/dist/types/extensions.d.ts +1 -1
- package/dist/types/manifest.d.ts +5 -1
- package/dist/types/readiness.d.ts +2 -0
- package/dist/types/types.d.ts +8 -1
- package/dist/types.js +8 -1
- package/dist/typescript-authoring.js +18 -7
- package/docs/AI-AUTHORING.md +10 -6
- package/docs/ASSETS.md +2 -1
- package/docs/AUTH-BACKUP.md +32 -0
- package/docs/BEST-PRACTICES.md +17 -9
- package/docs/CAPABILITIES.md +1 -1
- package/docs/CI-FOLLOWUP-2026-09-19.md +97 -0
- package/docs/CI-RELEASE-AUDIT-2026-09-19.md +322 -0
- package/docs/CI.md +8 -3
- package/docs/CODEBASE-AUDIT-2026-09-20.md +278 -0
- package/docs/DEVELOPMENT-PIPELINE.md +181 -0
- package/docs/EXTENSIONS.md +15 -5
- package/docs/FRAMEWORK.md +22 -18
- package/docs/FUNCTION-SECURITY.md +44 -0
- package/docs/LOCAL-DEVELOPMENT.md +3 -0
- package/docs/OPEN-DECISIONS.md +184 -212
- package/docs/OPERATIONAL-PROOF.md +3 -3
- package/docs/OPERATIONS.md +3 -3
- package/docs/POLICIES.md +13 -5
- package/docs/PRERENDER.md +23 -11
- package/docs/PROJECT-DIRECTION.md +3 -3
- package/docs/READINESS.md +6 -2
- package/docs/README.md +18 -41
- package/docs/RELEASE-0.4.0-alpha.3.md +50 -0
- package/docs/RELEASE-READINESS.md +6 -6
- package/docs/RELEASE-SECURITY.md +68 -168
- package/docs/RESILIENCE.md +3 -3
- package/docs/ROUTING.md +3 -4
- package/docs/SECURITY-AUDIT.md +2 -2
- package/docs/SPECIFICATION.md +7 -5
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +6 -5
- package/docs/SPIKE-BUSINESS-SUITE.md +14 -6
- package/docs/SPIKE-CORE-LAYERING.md +48 -17
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +7 -5
- package/docs/SPIKE-LAMBDA-COMPILE.md +8 -0
- package/docs/STATIC.md +14 -3
- package/docs/TOOLING.md +4 -3
- package/docs/TYPESCRIPT-AUTHORING.md +6 -1
- package/docs/VERSION-ALIGNMENT.md +46 -205
- package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +68 -0
- package/docs/archive/2026-09-19/MANAGEMENT-SECURITY.md +102 -0
- package/docs/{NEXT-PHASE-PLAN.md → archive/2026-09-19/NEXT-PHASE-PLAN.md} +15 -5
- package/docs/{NEXT-STEPS.md → archive/2026-09-19/NEXT-STEPS.md} +15 -3
- package/docs/archive/2026-09-19/OPEN-DECISIONS.md +277 -0
- package/docs/archive/2026-09-19/RELEASE-SECURITY.md +186 -0
- package/docs/archive/2026-09-19/ROADMAP.md +386 -0
- package/docs/{SPIKE-EXTENSION-MODEL.md → archive/2026-09-19/SPIKE-EXTENSION-MODEL.md} +10 -0
- package/docs/{SPIKE-EXTENSIONS.md → archive/2026-09-19/SPIKE-EXTENSIONS.md} +25 -14
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +776 -0
- package/docs/{USABILITY-REVIEW.md → archive/2026-09-19/USABILITY-REVIEW.md} +12 -2
- package/docs/archive/README.md +27 -0
- package/docs/policies/agents.md +1 -1
- package/docs/policies/compression.md +3 -2
- package/docs/policies/security.md +3 -2
- package/examples/assets/example.yaml +1 -1
- package/examples/cookbook/route-index.json +1 -1
- package/examples/cookbook/routes/middleware.yaml +1 -1
- package/examples/prerender/README.md +14 -6
- package/examples/prerender/functions/page.mjs +4 -2
- package/examples/prerender/middleware/template.mjs +1 -1
- package/examples/prerender/prerender.mjs +1 -1
- package/examples/prerender/urlcode.yaml +8 -4
- package/llms-full.txt +117 -45
- package/llms.txt +5 -4
- package/package.json +24 -4
- package/packaging/claude-plugin/.claude-plugin/plugin.json +2 -2
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +17 -19
- package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +9 -9
- package/recipes/authenticated-json-api/README.md +4 -3
- package/recipes/authenticated-json-api/functions/profile.mjs +2 -1
- package/recipes/authenticated-json-api/recipe.yaml +1 -1
- package/recipes/contact-form/functions/contact.mjs +2 -1
- package/recipes/contact-form/recipe.yaml +2 -2
- package/recipes/cors-api/README.md +2 -2
- package/recipes/cors-api/recipe.yaml +1 -1
- package/recipes/health-page/README.md +1 -1
- package/recipes/json-api/README.md +1 -1
- package/recipes/json-api/recipe.yaml +3 -3
- package/recipes/middleware/README.md +8 -4
- package/recipes/protected-download/README.md +1 -1
- package/recipes/protected-download/recipe.yaml +1 -1
- package/recipes/static-plus-api/README.md +2 -2
- package/recipes/static-plus-api/public/index.html +1 -1
- package/recipes/static-plus-api/recipe.yaml +1 -1
- package/recipes/static-plus-api/urlcode.yaml +1 -1
- package/recipes/typescript/recipe.yaml +4 -4
- package/skills/urlcode/SKILL.md +6 -6
- package/starters/default/AGENTS.md +6 -6
- package/docs/SPIKE-MONOREPO.md +0 -322
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Development and release pipeline
|
|
2
|
+
|
|
3
|
+
The repository uses npm workspaces, independent package versions, and Changesets
|
|
4
|
+
for UI, auth and admin. Core remains at the repository root and is explicitly
|
|
5
|
+
included in the shared release inventory. Moving it is not required to use the
|
|
6
|
+
same release checks and coordinator.
|
|
7
|
+
|
|
8
|
+
## Pull requests
|
|
9
|
+
|
|
10
|
+
Every PR starts `verify`. A complete Git diff selects one of two lanes:
|
|
11
|
+
|
|
12
|
+
- **Prose:** root project Markdown, `docs/**/*.md`, `llms.txt` and
|
|
13
|
+
`llms-full.txt` changes run guidance/generated-resource checks, runtime audit,
|
|
14
|
+
and the required container job. CodeQL retains its repository policy.
|
|
15
|
+
- **Full:** all other changes, mixed changes, empty/unavailable diffs and main
|
|
16
|
+
pushes run static checks once and core and workspace suites separately. Both
|
|
17
|
+
suites retain Linux on Node 22/24/26. Main adds Windows/macOS on Node 24.
|
|
18
|
+
PRs add those platform legs for runtime, CLI, SQLite, fixture, dependency,
|
|
19
|
+
workflow and unknown changes; known UI presentation-only changes omit them. Package, action,
|
|
20
|
+
cookbook, reproducibility and operational checks retain their coverage.
|
|
21
|
+
|
|
22
|
+
The prose allowlist is deliberately narrow. Package documentation, skills,
|
|
23
|
+
starters, examples, schemas, manifests and workflow changes select full checks.
|
|
24
|
+
A rename from source into docs also selects full checks. No required workflow
|
|
25
|
+
uses `paths-ignore`.
|
|
26
|
+
|
|
27
|
+
`verify-complete` accepts only the results specified by the successful plan.
|
|
28
|
+
Failed, canceled, missing or unexpectedly skipped work fails the gate. Required
|
|
29
|
+
check names (`verify-complete`, `container`) and CodeQL enforcement are preserved.
|
|
30
|
+
The repository ruleset currently does not require a branch to be up to date;
|
|
31
|
+
release publication separately requires verification of the exact main commit.
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm run check:docs # prose checks without the runtime suite
|
|
35
|
+
npm run ci:plan -- BASE_SHA HEAD_SHA
|
|
36
|
+
npm run ci:report -- RUN_ID # read GitHub job/step durations
|
|
37
|
+
npm run ci:history -- 100 2026-09-19 # group historical timing samples
|
|
38
|
+
npm run verify # full local validation remains available
|
|
39
|
+
npm run test:package # builds and installs a real archive
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
CI uses `test:package:built` only after building in that same job. Core tests and
|
|
43
|
+
workspace tests run in separate jobs to shorten their serial critical path;
|
|
44
|
+
this increases job setup overhead and needs monitoring for runner queue pressure.
|
|
45
|
+
After building all three extensions, the workspace job also runs the real
|
|
46
|
+
`init --with auth,admin,ui` scaffold integration. Missing workspace outputs fail
|
|
47
|
+
instead of silently skipping an absent external checkout.
|
|
48
|
+
The [audit](CI-RELEASE-AUDIT-2026-09-19.md) records the previous timings.
|
|
49
|
+
The [follow-up measurements](CI-FOLLOWUP-2026-09-19.md) record the first compact
|
|
50
|
+
main result and explain why the new lanes still need 20 organic runs each.
|
|
51
|
+
|
|
52
|
+
## Version and release ownership
|
|
53
|
+
|
|
54
|
+
A feature PR records release intent in a Changeset for a changed workspace
|
|
55
|
+
package. Review dependency/peer changes explicitly. Keep pre-mode enabled until
|
|
56
|
+
an explicit decision to leave alpha. Do not force all packages to one version.
|
|
57
|
+
Core version bumps remain explicit in the release PR, including its CLI banner.
|
|
58
|
+
`release:check` verifies every manifest against its lockfile entry and checks the
|
|
59
|
+
alpha-mode policy. Existing CLI tests catch core banner/version disagreement.
|
|
60
|
+
|
|
61
|
+
A release PR collects version/changelog and lockfile changes together. Ordinary
|
|
62
|
+
unreleased development does not move existing version tags or npm versions.
|
|
63
|
+
Use the exact release commit after its full platform checks have passed. Routine
|
|
64
|
+
main builds use five OS/Node combinations per suite (ten jobs total), rather
|
|
65
|
+
than the full nine per suite (eighteen). Nightly runs at 07:17 UTC and manual
|
|
66
|
+
runs retain all three operating systems on all three Node versions. Before a
|
|
67
|
+
release, run `gh workflow run ci.yml --ref main` and wait for that exact commit's
|
|
68
|
+
full run to succeed. A successful compact main run alone cannot authorize a
|
|
69
|
+
release. Main pushes do not cancel scheduled/manual verification.
|
|
70
|
+
|
|
71
|
+
Inspect release state:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
npm run release:status # registry channels, peer compatibility, remote tag SHAs
|
|
75
|
+
npm run release:plan # read-only JSON inventory, including root core
|
|
76
|
+
npm run release:run # read-only ordered proposal at HEAD
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
All three commands require network access; none publish. `release:run` selects
|
|
80
|
+
unpublished manifest versions and releases already tagged at HEAD (for resuming
|
|
81
|
+
partial completion). Its order is core, UI, auth, admin, skipping other already
|
|
82
|
+
published versions. Plan fields come from package manifests, not copied versions
|
|
83
|
+
in another config file.
|
|
84
|
+
|
|
85
|
+
Once the release itself is authorized, from a clean checkout of that exact SHA:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
npm run release:run -- --execute
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This opt-in command creates missing tags through GitHub, refuses an existing tag
|
|
92
|
+
at a different commit, and waits for each package's tag-triggered release before
|
|
93
|
+
starting the next. It stops on failure or missing npm publication. Do not push
|
|
94
|
+
all release tags at once: publication workflows share one concurrency group and
|
|
95
|
+
GitHub may replace pending runs. The coordinator intentionally starts one at a
|
|
96
|
+
time. It never merges a PR, bypasses checks, force-pushes or publishes locally.
|
|
97
|
+
|
|
98
|
+
The coordinator needs `gh` authentication with repository Contents write and
|
|
99
|
+
Actions read; reading checks also needs Checks read. It is intended for an
|
|
100
|
+
authorized maintainer session or a repository-scoped GitHub App. A fine-grained
|
|
101
|
+
PAT can serve a short-lived maintainer script with those permissions, but must
|
|
102
|
+
have no ruleset bypass. The workflow `GITHUB_TOKEN` should not be used to create
|
|
103
|
+
these trigger tags: its push events do not start another ordinary push workflow.
|
|
104
|
+
For long-lived automation prefer a GitHub App; do not add a bypass to `main`.
|
|
105
|
+
[GitHub App guidance](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/deciding-when-to-build-a-github-app)
|
|
106
|
+
|
|
107
|
+
## Publication and recovery
|
|
108
|
+
|
|
109
|
+
The four workflow filenames remain unchanged because npm trusted publishing
|
|
110
|
+
names them. They call shared helpers for identity, preflight, peer installation,
|
|
111
|
+
retry handling and publication. npm authentication remains OIDC; no npm token
|
|
112
|
+
is introduced. Core candidate and release share `prepare-core-release.sh`.
|
|
113
|
+
For an authorized release, first dispatch the manual `candidate.yml` workflow on
|
|
114
|
+
the selected main commit. It extends the core candidate with UI/auth/admin
|
|
115
|
+
archives and verifies all four together in an isolated temporary consumer:
|
|
116
|
+
peer compatibility, installed versions, public imports and real scaffold
|
|
117
|
+
composition. Its signed `train.json` records the proposed archives and integrity.
|
|
118
|
+
A candidate does not publish, validate live providers or prove registry OIDC;
|
|
119
|
+
release workflows still prepare and retain their own immutable retry artifacts.
|
|
120
|
+
Extensions share `prepare-extension-release.sh` and test published peer floors.
|
|
121
|
+
Auth/admin build in the workspace for packaging, then build and run their suites
|
|
122
|
+
in a temporary copy outside the monorepo against exact registry peer floors.
|
|
123
|
+
This preserves #184’s isolation fix; npm `--prefix` is not an isolation boundary.
|
|
124
|
+
|
|
125
|
+
Preflight checks the checkout SHA, main ancestry, a successful exact-SHA full
|
|
126
|
+
`ci.yml` nightly or explicit manual run, CodeQL, remote tag SHA, npm
|
|
127
|
+
channel monotonicity and published peer floors. When a full run was canceled,
|
|
128
|
+
run `verify` manually at the selected tag/ref, then rerun the failed release;
|
|
129
|
+
never substitute another commit's passing run or move the tag.
|
|
130
|
+
|
|
131
|
+
Prepared artifacts are retained for 90 days before publication. A rerun of the
|
|
132
|
+
same workflow run restores those original bytes and skips preparation. npm
|
|
133
|
+
versions already present must have identical SHA-512 integrity; existing GitHub
|
|
134
|
+
assets must match byte for byte. Different bytes stop the release. An absent or
|
|
135
|
+
expired artifact requires reconstruction that still passes these comparisons;
|
|
136
|
+
if it cannot, diagnose and create a new version rather than overwrite history.
|
|
137
|
+
|
|
138
|
+
GitHub release classification follows the manifest's prerelease status. New
|
|
139
|
+
GitHub releases use `--latest=false`; stable latest promotion is a separate
|
|
140
|
+
maintainer decision, avoiding accidental promotion by a package-level release.
|
|
141
|
+
GHCR updates the derived channel (`alpha` for alphas, `latest` for stable) and
|
|
142
|
+
preserves existing version images only when their source label matches. An
|
|
143
|
+
existing image/channel without the required labels fails closed and needs a
|
|
144
|
+
reviewed migration; this change does not silently relabel old images.
|
|
145
|
+
|
|
146
|
+
Partial npm/GitHub/GHCR success is possible; those systems cannot be updated
|
|
147
|
+
atomically. Rerun the original failed run, check its summary and then rerun the
|
|
148
|
+
coordinator. A changed source commit requires a new version and tag. Failed
|
|
149
|
+
OIDC configuration needs correction on npm, not tag deletion. A green dry run
|
|
150
|
+
cannot prove registry-side OIDC trust; each package's first authorized publish
|
|
151
|
+
must verify it.
|
|
152
|
+
|
|
153
|
+
Historical tags, GitHub release flags and npm channels are not retroactively
|
|
154
|
+
rewritten by these scripts. Use `release:status` to inspect them. The active [Immutable release tags rule](https://github.com/jimhoyd-com/urlcode/rules/23712319)
|
|
155
|
+
prohibits update/deletion of `v*` and `@jimhoyd/urlcode-*@*` tags, permits
|
|
156
|
+
creation, and has no bypass actors. Its reviewed configuration is tracked in
|
|
157
|
+
`.github/rulesets/release-tags.json`. Main protection is unchanged.
|
|
158
|
+
|
|
159
|
+
During the September 19 alignment, GitHub releases `v0.4.0-alpha.1` and
|
|
160
|
+
`v0.4.0-alpha.2` were explicitly marked as prereleases and GitHub latest was
|
|
161
|
+
restored to `v0.3.0`, matching npm. Their tags and artifact bytes were unchanged.
|
|
162
|
+
Further performance and release-train validation is tracked in
|
|
163
|
+
[issue #185](https://github.com/jimhoyd-com/urlcode/issues/185).
|
|
164
|
+
|
|
165
|
+
### Windows fixture cleanup
|
|
166
|
+
|
|
167
|
+
Auth/admin tests register resources with their package-local `test/cleanup.ts`.
|
|
168
|
+
Cleanup runs in reverse acquisition order: close servers and SQLite services
|
|
169
|
+
before deleting temporary directories, including services reopened by a test.
|
|
170
|
+
Every registered callback is attempted even if another closer throws, and the
|
|
171
|
+
combined error fails the test. Register each closer as soon as its resource opens.
|
|
172
|
+
The suites use a five-minute test-file timeout so a stuck fixture is diagnosed
|
|
173
|
+
before the CI job limit. Node applies this limit to whole test files too; the
|
|
174
|
+
large auth-core file legitimately exceeds two minutes on Windows Node 22.
|
|
175
|
+
Windows regression coverage runs on Node 24 for platform-sensitive PRs and
|
|
176
|
+
main; nightly/manual runs cover Node 22/24/26.
|
|
177
|
+
|
|
178
|
+
Failed auth service initialization also waits for its SQLite worker to terminate
|
|
179
|
+
before rejecting. Callers can clean up or retry after a rejected open without
|
|
180
|
+
racing a remaining database handle; configuration identity failures still fail
|
|
181
|
+
closed with the same error code.
|
package/docs/EXTENSIONS.md
CHANGED
|
@@ -6,10 +6,11 @@ and admin implementations live in `urlcode-auth` and `urlcode-admin`; the runtim
|
|
|
6
6
|
supplies only the generic integration contract. No project file can import a host
|
|
7
7
|
extension or choose its npm package.
|
|
8
8
|
|
|
9
|
-
Stored short links
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
no longer has a native `link`
|
|
9
|
+
Stored short links moved out of core this way too: a `urlcode-dynamic-link`
|
|
10
|
+
package (mount-based, like `auth`/`admin`) owned the durable link store, its CLI
|
|
11
|
+
and management API. That package has since been retired and unpublished, so no
|
|
12
|
+
supported stored-link extension ships today. Core no longer has a native `link`
|
|
13
|
+
handler or a `dynamicLinks` project flag.
|
|
13
14
|
|
|
14
15
|
A project declares versioned configuration and exclusive route mounts:
|
|
15
16
|
|
|
@@ -256,7 +257,16 @@ each half of a hook's `sandbox: true` opt-in has its own answer:
|
|
|
256
257
|
or provided: this is ordinary first-party project code, and the
|
|
257
258
|
extension's own `activate()` already has `ExtensionActivation.root` to
|
|
258
259
|
resolve the hook's `source` against and can `import()` it directly, the
|
|
259
|
-
same way any trusted `function`/`middleware` route does.
|
|
260
|
+
same way any trusted `function`/`middleware` route does. Do that import
|
|
261
|
+
with a per-activation cache-busting query, the way core's own trusted
|
|
262
|
+
route activation does (`src/trusted-functions.ts`): Node's ESM loader
|
|
263
|
+
caches a resolved module forever by URL, so a plain `import()` of the
|
|
264
|
+
unchanged file URL makes a second activation in the same process keep
|
|
265
|
+
serving the hook code that was on disk at the first one
|
|
266
|
+
(jimhoyd-com/urlcode#198). Only the hook's **entry** module is refreshed
|
|
267
|
+
this way — modules the hook itself imports stay on Node's module cache,
|
|
268
|
+
the same limitation the trusted route path has, so a change to a hook's
|
|
269
|
+
own dependency still needs a process restart.
|
|
260
270
|
- **Sandboxed (`sandbox: true`).** `@jimhoyd/urlcode/sandbox` exports
|
|
261
271
|
`SandboxPool`, the same QuickJS/worker-thread engine that backs a
|
|
262
272
|
sandboxed `function`/`middleware` route — the identical module-allowlist
|
package/docs/FRAMEWORK.md
CHANGED
|
@@ -56,9 +56,9 @@ Each rung's YAML is valid on every rung above it.
|
|
|
56
56
|
project copy, template and stylesheet overrides for kit-rendered pages.
|
|
57
57
|
|
|
58
58
|
Stored short links previously sat here as a native `link` route; that handler
|
|
59
|
-
was removed from core. A
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
was removed from core. A `urlcode-dynamic-link` package owned them the same way
|
|
60
|
+
`auth`/`admin` own their mounts, but it has been retired and unpublished; no
|
|
61
|
+
package occupies this rung today.
|
|
62
62
|
|
|
63
63
|
Rungs 1 to 3 need only the core package. Rungs 4 to 6 need the extension
|
|
64
64
|
packages, installed from npm as `0.1.0-alpha.x` prereleases, and a Node host
|
|
@@ -74,9 +74,9 @@ npm install @jimhoyd/urlcode @jimhoyd/urlcode-ui @jimhoyd/urlcode-auth @jimhoyd/
|
|
|
74
74
|
urlcode init my-site --with auth,admin
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
Installing from npm is the normal path;
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
Installing from npm is the normal path; `scripts/pack-sources.mjs` still builds
|
|
78
|
+
local tarballs from a reviewed checkout for operators who install only source
|
|
79
|
+
they have read — one revision now covers core and every extension. Three files make an extended
|
|
80
80
|
project. Nothing else is discovered by convention.
|
|
81
81
|
|
|
82
82
|
```
|
|
@@ -108,7 +108,8 @@ routes:
|
|
|
108
108
|
auth: {}
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
The operator host
|
|
111
|
+
The operator host explicitly registers the packages. Registration is an
|
|
112
|
+
activation boundary; it does not isolate trusted application code from the host:
|
|
112
113
|
|
|
113
114
|
```js
|
|
114
115
|
import { createUiExtension } from '@jimhoyd/urlcode-ui/host';
|
|
@@ -128,12 +129,12 @@ export default {
|
|
|
128
129
|
};
|
|
129
130
|
```
|
|
130
131
|
|
|
131
|
-
Auth and admin
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
`
|
|
135
|
-
|
|
136
|
-
|
|
132
|
+
Auth and admin already render their screens through `ui.kit` when the host
|
|
133
|
+
supplies the UI extension; their package-owned templates and catalogues must be
|
|
134
|
+
registered with that kit. Without it, both retain shared primitive rendering
|
|
135
|
+
through `presentation`. The example above uses that primitive fallback. The UI
|
|
136
|
+
block is optional, and a migration to the kit is not unfinished framework work.
|
|
137
|
+
See each package README for its complete kit registration example.
|
|
137
138
|
|
|
138
139
|
```sh
|
|
139
140
|
urlcode serve --project /absolute/site --host-file /absolute/operator/host.mjs --origin https://site.example
|
|
@@ -155,7 +156,8 @@ or mounts changes the revision and needs an explicit operator reapproval.
|
|
|
155
156
|
These are the facts that keep generated projects valid. The full matrix is in
|
|
156
157
|
[AI authoring](AI-AUTHORING.md); this is the short list.
|
|
157
158
|
|
|
158
|
-
- **YAML
|
|
159
|
+
- **Extension YAML names logical extensions, not host packages or credentials.**
|
|
160
|
+
Function and middleware `source` fields do name project modules. Extensions are
|
|
159
161
|
logical names; the host file chooses the implementation. There is no
|
|
160
162
|
`--extension` flag, no `import` in YAML, no interpolation.
|
|
161
163
|
- **One handler per route.** `redirect`, `respond`, `page`, `static`, `download`,
|
|
@@ -166,13 +168,15 @@ These are the facts that keep generated projects valid. The full matrix is in
|
|
|
166
168
|
It runs in-process with full Node access unless the route declares
|
|
167
169
|
`sandbox: true`, which isolates it to a text/JSON `Request`/`Response`
|
|
168
170
|
subset, validated `args` and granted `env`, with no `fetch`, Node,
|
|
169
|
-
filesystem or timers. Either way, `args`/`env`/`secrets` are exactly what
|
|
171
|
+
filesystem or general network access; bounded timers are available. Either way, `args`/`env`/`secrets` are exactly what
|
|
170
172
|
the route declares and an operator grants — trust changes where code runs,
|
|
171
173
|
not what it is handed. See docs/SPIKE-DEFAULT-TRUST-MODEL.md and
|
|
172
174
|
docs/FUNCTION-SECURITY.md.
|
|
173
175
|
- **Authentication is host processing.** Do not build login forms, session
|
|
174
|
-
cookies or password checks in functions.
|
|
175
|
-
|
|
176
|
+
cookies or password checks in functions. With the auth extension declared,
|
|
177
|
+
prefer `auth: true` or `auth: {role: admin}`; these expand to
|
|
178
|
+
`policies.extensions.auth`. The runtime filters credential headers passed to
|
|
179
|
+
application handlers. This is not a security boundary against trusted Node code.
|
|
176
180
|
- **Everything is validated before it runs.** `urlcode validate --local`,
|
|
177
181
|
`urlcode test`, `urlcode audit --expect-routes N`. Unsupported features fail
|
|
178
182
|
with the route named; nothing degrades silently.
|
|
@@ -194,5 +198,5 @@ These are the facts that keep generated projects valid. The full matrix is in
|
|
|
194
198
|
| Add accounts | [auth README](https://github.com/jimhoyd-com/urlcode-auth#readme), [auth security](https://github.com/jimhoyd-com/urlcode-auth/blob/main/SECURITY.md) |
|
|
195
199
|
| Add administration | [admin README](https://github.com/jimhoyd-com/urlcode-admin#readme) |
|
|
196
200
|
| Restyle every page | [ui README](https://github.com/jimhoyd-com/urlcode-ui#readme), [ui contract](https://github.com/jimhoyd-com/urlcode-ui/blob/main/CONTRACT.md) |
|
|
197
|
-
| Write an extension | [extensions](EXTENSIONS.md), [extension model review](SPIKE-EXTENSION-MODEL.md) |
|
|
201
|
+
| Write an extension | [extensions](EXTENSIONS.md), [extension model review](archive/2026-09-19/SPIKE-EXTENSION-MODEL.md) |
|
|
198
202
|
| Run it | [operations](OPERATIONS.md), [install](INSTALL.md), [deployment checks](DEPLOYMENT-CHECKS.md) |
|
|
@@ -40,6 +40,35 @@ API is all it has, so its binding grant *is* effectively its whole reach into
|
|
|
40
40
|
the environment. Trusted code's reach is not bounded that way; treat the
|
|
41
41
|
grant as scoping `context`, not as scoping the process.
|
|
42
42
|
|
|
43
|
+
## Migrating to the trusted default
|
|
44
|
+
|
|
45
|
+
If you are upgrading a project from a release before this change shipped:
|
|
46
|
+
**every existing `function` and `middleware` route silently changes execution
|
|
47
|
+
mode**, from sandboxed to trusted, unless it already has (or you add)
|
|
48
|
+
`sandbox: true`. This is a real behavior change on upgrade, not a
|
|
49
|
+
documentation update — a route that used to run with no filesystem or network
|
|
50
|
+
access will, after the upgrade, run with full Node access unless you opt it
|
|
51
|
+
back into the sandbox.
|
|
52
|
+
|
|
53
|
+
Before upgrading:
|
|
54
|
+
|
|
55
|
+
- List every `function` and `middleware` route in the project.
|
|
56
|
+
- For each one, decide whether you fully trust that code to run in-process
|
|
57
|
+
with full Node/filesystem/network access — the same trust you would extend
|
|
58
|
+
to any other code you deploy to that server.
|
|
59
|
+
- Add `sandbox: true` explicitly to any route whose code you do not fully
|
|
60
|
+
trust, that processes input from a source you don't control, or that handles
|
|
61
|
+
a secret binding you want isolated — before you upgrade, not after.
|
|
62
|
+
- Routes you do want running trusted need no change; that is now the default.
|
|
63
|
+
|
|
64
|
+
The change moves the sandbox from an unconditional guarantee to an explicit,
|
|
65
|
+
per-route choice, mainly for performance: the previous blanket sandbox capped
|
|
66
|
+
concurrency at two workers with no queue shared across every function route on
|
|
67
|
+
the server, which does not scale to real concurrent traffic. It also brings
|
|
68
|
+
first-party code in line with how the rest of the Node ecosystem treats
|
|
69
|
+
deployed application code. The sandbox itself is unchanged for routes that opt
|
|
70
|
+
into it; only the default for routes that declare neither option has changed.
|
|
71
|
+
|
|
43
72
|
## What "sandboxed" (`sandbox: true`) still guarantees
|
|
44
73
|
|
|
45
74
|
- Function sources are parsed/snapshotted without importing them into Node.
|
|
@@ -116,6 +145,21 @@ it is handed *through `context`*. It does not change what the code can go get
|
|
|
116
145
|
on its own once it is running; see "binding grants are unaffected" above for
|
|
117
146
|
that distinction.
|
|
118
147
|
|
|
148
|
+
## Trusted code, not trusted requests
|
|
149
|
+
|
|
150
|
+
"Trusted" describes the code's authorship — first-party project code you
|
|
151
|
+
reviewed and deployed — not the requests it handles. Every request, in either
|
|
152
|
+
mode, still carries client-controlled path, query, header and body data that is
|
|
153
|
+
exactly as adversarial as it always was. Running trusted means that code
|
|
154
|
+
executes with full Node access if it mishandles that input; it does not mean
|
|
155
|
+
the input itself became safe to trust. Declare `parameters` and `request.body`
|
|
156
|
+
validation in YAML, check `args` and any other request data again inside
|
|
157
|
+
function/middleware code, and implement your own authentication and
|
|
158
|
+
authorization — no route, sandboxed or trusted, adds automatic auth.
|
|
159
|
+
`sandbox: true` narrows what a bug or an unreviewed dependency in the *code*
|
|
160
|
+
can do with that same request data; it is not a substitute for validating or
|
|
161
|
+
authenticating the request itself.
|
|
162
|
+
|
|
119
163
|
## Granting selected bindings
|
|
120
164
|
|
|
121
165
|
An application may request a named binding in YAML, but only an operator can
|
|
@@ -10,6 +10,9 @@ ship, together with the declarations; `dist` is never committed. Make is an opti
|
|
|
10
10
|
shortcut layer; npm and the CLI work on Windows, macOS and Linux. No global
|
|
11
11
|
package install, hosting account, database or Docker is needed for the local loop.
|
|
12
12
|
|
|
13
|
+
For the repository's prose-only checks, CI selection and release helper commands,
|
|
14
|
+
see [the development pipeline](DEVELOPMENT-PIPELINE.md).
|
|
15
|
+
|
|
13
16
|
## Try the runtime
|
|
14
17
|
|
|
15
18
|
From the runtime checkout, `make dev` installs locked dependencies if needed and
|