@jimhoyd/urlcode 0.4.0-alpha.3 → 0.4.1
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-plugin/marketplace.json +1 -1
- package/README.md +20 -15
- package/ROADMAP.md +16 -11
- package/dist/BUILD-MANIFEST.json +16 -15
- package/dist/authoring.js +15 -1
- package/dist/capability-query.js +0 -1
- package/dist/catalog.js +0 -1
- package/dist/cli.js +23 -7
- package/dist/config.js +1 -1
- package/dist/explain.js +1 -1
- package/dist/http-response.js +1 -1
- package/dist/index.js +1 -0
- package/dist/init-with.js +36 -11
- package/dist/manifest.js +1 -1
- package/dist/mcp-authoring.js +2 -2
- package/dist/mcp.js +1 -1
- package/dist/policies/cache.js +2 -2
- package/dist/project-dependencies.js +305 -0
- package/dist/runtime.js +1 -1
- package/dist/trusted-functions.js +4 -5
- package/dist/types/authoring.d.ts +9 -1
- package/dist/types/capability-query.d.ts +0 -1
- package/dist/types/catalog.d.ts +0 -4
- package/dist/types/config.d.ts +1 -9
- package/dist/types/explain.d.ts +0 -1
- package/dist/types/http-response.d.ts +0 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/init-with.d.ts +7 -13
- package/dist/types/manifest.d.ts +0 -1
- package/dist/types/project-dependencies.d.ts +78 -0
- package/dist/types/trusted-functions.d.ts +1 -4
- package/docs/AI-AUTHORING.md +5 -1
- package/docs/AWS.md +9 -0
- package/docs/CI-FOLLOWUP-2026-09-19.md +1 -1
- package/docs/CODEBASE-AUDIT-2026-09-20.md +6 -0
- package/docs/COMPOSING-A-SITE.md +278 -0
- package/docs/DEVELOPMENT-PIPELINE.md +208 -119
- package/docs/EXTENSIONS.md +36 -6
- package/docs/FRAMEWORK.md +45 -30
- package/docs/INSTALL.md +13 -8
- package/docs/MIDDLEWARE.md +10 -4
- package/docs/OPEN-DECISIONS.md +46 -6
- package/docs/READINESS.md +4 -3
- package/docs/README.md +3 -4
- package/docs/RELEASE-0.4.1.md +73 -0
- package/docs/RELEASE-SECURITY.md +27 -12
- package/docs/SPECIFICATION.md +5 -1
- package/docs/SPIKE-CORE-LAYERING.md +1 -1
- package/docs/STARTERS.md +17 -5
- package/docs/TOOLING.md +6 -4
- package/docs/VERCEL.md +10 -2
- package/docs/VERSION-ALIGNMENT.md +42 -8
- package/docs/archive/2026-09-19/ROADMAP.md +1 -0
- package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +1 -0
- package/docs/{SPIKE-LAMBDA-COMPILE.md → archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md} +168 -12
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +2 -0
- package/docs/archive/README.md +1 -0
- package/docs/yaml/functions.md +10 -2
- package/docs/yaml/middleware.md +5 -3
- package/examples/cookbook/middleware/envelope.mjs +4 -2
- package/llms-full.txt +387 -44
- package/llms.txt +1 -0
- package/package.json +8 -5
- package/packaging/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/recipes/middleware/middleware/envelope.mjs +4 -2
package/docs/INSTALL.md
CHANGED
|
@@ -8,8 +8,13 @@ so the installed `urlcode` command runs `dist/cli.js` and needs no build tool.
|
|
|
8
8
|
|
|
9
9
|
## npm
|
|
10
10
|
|
|
11
|
+
The stable release target is `0.4.1`. Use these commands after publication;
|
|
12
|
+
`npm run release:status` in a checkout reports live registry availability. An
|
|
13
|
+
unversioned npm install selects the current `latest` channel, while `@alpha`
|
|
14
|
+
explicitly selects the separate prerelease channel.
|
|
15
|
+
|
|
11
16
|
```sh
|
|
12
|
-
npm install --global @jimhoyd/urlcode
|
|
17
|
+
npm install --global @jimhoyd/urlcode@0.4.1
|
|
13
18
|
urlcode --help
|
|
14
19
|
```
|
|
15
20
|
|
|
@@ -19,12 +24,12 @@ Which dependency list it belongs in depends on how the project uses URLCode:
|
|
|
19
24
|
```sh
|
|
20
25
|
# Using URLCode as a tool: validate, test and build in CI, never imported by
|
|
21
26
|
# the code that serves requests.
|
|
22
|
-
npm install --save-dev @jimhoyd/urlcode
|
|
27
|
+
npm install --save-dev --save-exact @jimhoyd/urlcode@0.4.1
|
|
23
28
|
npx urlcode validate
|
|
24
29
|
|
|
25
30
|
# Embedding the runtime (see TYPESCRIPT.md): the application imports
|
|
26
31
|
# @jimhoyd/urlcode at startup, so it must survive `npm ci --omit=dev`.
|
|
27
|
-
npm install --save @jimhoyd/urlcode
|
|
32
|
+
npm install --save --save-exact @jimhoyd/urlcode@0.4.1
|
|
28
33
|
```
|
|
29
34
|
|
|
30
35
|
A devDependency is absent from a production install, so an application that
|
|
@@ -65,7 +70,7 @@ It downloads the release tarball, verifies its SHA-256 against the release's
|
|
|
65
70
|
`SHA256SUMS`, and installs with npm. Options:
|
|
66
71
|
|
|
67
72
|
```sh
|
|
68
|
-
curl -fsSL .../install.sh | sh -s -- --version 0.
|
|
73
|
+
curl -fsSL .../install.sh | sh -s -- --version 0.4.1 --prefix "$HOME/.local"
|
|
69
74
|
```
|
|
70
75
|
|
|
71
76
|
`--prefix` avoids needing privileges for a global npm directory; add
|
|
@@ -76,12 +81,12 @@ for that moment: to inspect first, download it, read it, then run it.
|
|
|
76
81
|
|
|
77
82
|
No image is published yet: the release job's GHCR step is gated behind the
|
|
78
83
|
`PUBLISH_CONTAINER` repository variable and has not run, so there is nothing at
|
|
79
|
-
`ghcr.io/jimhoyd-com/urlcode` to pull.
|
|
84
|
+
`ghcr.io/jimhoyd-com/urlcode` to pull. After the release tag exists, build it from that checkout:
|
|
80
85
|
|
|
81
86
|
```sh
|
|
82
|
-
git clone --branch v0.
|
|
83
|
-
docker build -t urlcode:0.
|
|
84
|
-
docker run --rm -p 127.0.0.1:3000:3000 -v "$PWD:/project:ro" urlcode:0.
|
|
87
|
+
git clone --branch v0.4.1 https://github.com/jimhoyd-com/urlcode.git
|
|
88
|
+
docker build -t urlcode:0.4.1 urlcode
|
|
89
|
+
docker run --rm -p 127.0.0.1:3000:3000 -v "$PWD:/project:ro" urlcode:0.4.1 \
|
|
85
90
|
serve --project /project --host 0.0.0.0
|
|
86
91
|
```
|
|
87
92
|
|
package/docs/MIDDLEWARE.md
CHANGED
|
@@ -48,12 +48,18 @@ Every middleware and the function share the same request and context. Validated
|
|
|
48
48
|
with a fresh `context.state` object for this chain. State never survives the
|
|
49
49
|
request. Header edits are visible downstream; editing the request does not
|
|
50
50
|
reroute it or change already validated inputs. Request bodies are single-use:
|
|
51
|
-
reading one consumes it for downstream code.
|
|
52
|
-
API
|
|
51
|
+
reading one consumes it for downstream code. On a `sandbox: true` route there is
|
|
52
|
+
no `clone()` or streaming API at all, so pass parsed data through
|
|
53
|
+
`context.state`; a trusted route receives Node's own `Request`/`Response` and so
|
|
54
|
+
does have `clone()`, but passing parsed data through `context.state` keeps the
|
|
55
|
+
chain portable between the two modes.
|
|
53
56
|
|
|
54
57
|
Function responses support the existing text/JSON guest API. To transform their
|
|
55
|
-
body, read it and return a new `Response`.
|
|
56
|
-
download bodies are opaque and cannot be read
|
|
58
|
+
body, read it and return a new `Response`. On a `sandbox: true` route, native
|
|
59
|
+
redirect/respond/page/static/download bodies are opaque and cannot be read
|
|
60
|
+
through `text()` or `json()`; a trusted chain receives them as an ordinary
|
|
61
|
+
`Response` and can read them, so wrapping `respond: {text: hello}` and returning
|
|
62
|
+
`HELLO` works there and fails in the guest.
|
|
57
63
|
Returning the same native response preserves original bytes, including binary
|
|
58
64
|
files, ranges and HEAD lengths. You may add headers, but cannot change its
|
|
59
65
|
original status or existing native headers while preserving that body. To replace
|
package/docs/OPEN-DECISIONS.md
CHANGED
|
@@ -33,9 +33,8 @@ keeps earlier discussions. Recommendations below are not accepted decisions.
|
|
|
33
33
|
|---|---|---|
|
|
34
34
|
| Where does work status live? | Several old plans repeated issues and continued calling delivered work unfinished. | Issues for actionable status, this short roadmap for sequence, archive for completed proposals. Preserve evidence gaps when archiving. |
|
|
35
35
|
| Expand into business applications now? | No collection handler or proposed business suite is implemented; the model-backed benchmark evidence is missing. | Measure existing tasks and record repeated application plumbing before selecting a collection/CMS/forms project. Retired short-link products stay retired. [Proposal](SPIKE-BUSINESS-SUITE.md). |
|
|
36
|
-
| Which provider execution model next? | AWS/Vercel still reject function/middleware despite the trusted default. | Decide demand first, then compare one Node deployment per project against one Lambda per route. Do not promise either today. [Proposal](SPIKE-LAMBDA-COMPILE.md). |
|
|
37
36
|
| Fold extension schemas into retrieved context? | `urlcode extensions` and the MCP `get_extensions` query return the registered configuration and policy schemas, but `src/context.ts:113` reports `extensions` as names only, so an author writing `extensions.<name>.config` or `policies.extensions.<name>` must run the separate operator-authorized query first. | Decide from retrieval and task evidence, not preference: the existing small-task harness can measure whether folding schemas into bounded context improves authoring. Keep the token budget bounded and never auto-load a project-selected host file. This is a discovery improvement, not a defect in the existing query. |
|
|
38
|
-
|
|
|
37
|
+
| Keep the POST-plus-`request.body` sandbox advisory? | `src/readiness.ts` nudges any code-running route that accepts POST with a declared `request.body` and declares neither `sandbox: true` nor `sandboxReason`. It is advisory only: never fails `audit`, never changes `ready`. | The nudge keys on request *shape* while [AI authoring](AI-AUTHORING.md) tells authors to decide on *code* trust, so it can read as "untrusted input implies sandbox" -- the reasoning that guidance explicitly rejects. It still has value as a prompt to record a decision. Recommendation: keep the trigger, restate the message as a request to record the trust decision (`sandbox: true` or `sandboxReason`) rather than as a suggestion that this route may need isolation. Not changed here; #196 was a docs/tooling alignment pass. |
|
|
39
38
|
|
|
40
39
|
The broader [AI benchmark proposal](SPIKE-AI-FRAMEWORK-BENCHMARK.md) also needs a
|
|
41
40
|
chosen application, model-run budget and execution authorization. The existing
|
|
@@ -48,6 +47,10 @@ small-task harness can supply evidence without committing to that larger study.
|
|
|
48
47
|
- Core `0.4.0-alpha.2` and current extension releases exist; publishing that
|
|
49
48
|
already-shipped version is not a next step.
|
|
50
49
|
- Auth/admin kit adoption and shared form helpers are implemented in their code.
|
|
50
|
+
- The UI primitive fallback is retired, which settles the question this table
|
|
51
|
+
carried. Auth and admin now render every screen through the kit and refuse
|
|
52
|
+
activation without it (`packages/auth/src/auth.ts`: "there is no
|
|
53
|
+
shared-primitive fallback"), so "keep both" no longer describes the code.
|
|
51
54
|
- The template pins `0.4.0-alpha.2`. Its skill differences were read against that
|
|
52
55
|
pin: omitted handlers and advice about the removed management API are stale,
|
|
53
56
|
not intentional older-version behavior.
|
|
@@ -55,6 +58,44 @@ small-task harness can supply evidence without committing to that larger study.
|
|
|
55
58
|
is not a schema validator for every example. Extending its coverage is tracked
|
|
56
59
|
separately, not a reason to weaken review or bypass required checks.
|
|
57
60
|
|
|
61
|
+
## Accepted: one Node deployment per project
|
|
62
|
+
|
|
63
|
+
**Decided 2026-09-19.** Projects that use `function` or `middleware` deploy as
|
|
64
|
+
**one trusted Node process** — a container or a VM running the project as it
|
|
65
|
+
runs locally. That is the supported execution model, and it needs no new work:
|
|
66
|
+
it is what the runtime already does.
|
|
67
|
+
|
|
68
|
+
**Per-route Lambda compilation is not pursued.** The alternative on the table
|
|
69
|
+
was a build step emitting one Lambda per `function` route
|
|
70
|
+
([the proposal](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md)). It is declined for now, on three
|
|
71
|
+
grounds the proposal itself states:
|
|
72
|
+
|
|
73
|
+
1. It would replace the sandbox guarantee rather than preserve it, and lose the
|
|
74
|
+
fresh-per-invocation state that `sandbox: true` currently guarantees.
|
|
75
|
+
2. It would make this project the author of generated IAM roles — a
|
|
76
|
+
security-critical output it has never owned.
|
|
77
|
+
3. It would trade an honest refusal for a larger claim nobody has deployed.
|
|
78
|
+
|
|
79
|
+
Against that, a single Node deployment supports every route type today with no
|
|
80
|
+
compiler, no generated infrastructure and no second isolation story to document.
|
|
81
|
+
|
|
82
|
+
**What follows from this decision:**
|
|
83
|
+
|
|
84
|
+
- AWS and Vercel continue to refuse `function` and `middleware` at activation,
|
|
85
|
+
naming the route (`src/capabilities.ts`, `activateNativeOnly` in
|
|
86
|
+
`src/adapters.ts`). That refusal is now a **deliberate position**, not a gap
|
|
87
|
+
awaiting an adapter. Documentation should say so rather than implying the
|
|
88
|
+
support is coming.
|
|
89
|
+
- Serverless targets remain first-class for the declarative route types they can
|
|
90
|
+
actually serve; nothing about static or native-only deployment changes.
|
|
91
|
+
- [SPIKE-LAMBDA-COMPILE.md](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md) is kept as the analysis
|
|
92
|
+
behind this decision, not as a plan. Reopen it only on evidence of real demand
|
|
93
|
+
for URLCode `function` routes specifically on AWS serverless — the proposal's
|
|
94
|
+
own §6 already scopes what a first attempt would be.
|
|
95
|
+
|
|
96
|
+
This decision is about the *execution model*, not about AWS. Deploying the Node
|
|
97
|
+
process to AWS (ECS, EC2, App Runner) is an operator choice this fully supports.
|
|
98
|
+
|
|
58
99
|
## Accepted: per-package release tags
|
|
59
100
|
|
|
60
101
|
**Decided 2026-09-19.** Workspace packages under `packages/` release on
|
|
@@ -160,10 +201,9 @@ moves rather than trusting this line.
|
|
|
160
201
|
The [archived monorepo plan](archive/2026-09-19/SPIKE-MONOREPO.md) records
|
|
161
202
|
migration context.
|
|
162
203
|
[Issue 172](https://github.com/jimhoyd-com/urlcode/issues/172), which tracked
|
|
163
|
-
"consolidate middleware into core after moving it into the monorepo,"
|
|
164
|
-
**
|
|
165
|
-
|
|
166
|
-
it has landed.
|
|
204
|
+
"consolidate middleware into core after moving it into the monorepo," was
|
|
205
|
+
**closed on 2026-09-19** as moot — there was nothing left to consolidate.
|
|
206
|
+
Migration starting is not a claim that it has landed.
|
|
167
207
|
|
|
168
208
|
## Source review baseline
|
|
169
209
|
|
package/docs/READINESS.md
CHANGED
|
@@ -10,9 +10,10 @@ urlcode audit --project ../my-links --expect-routes 2
|
|
|
10
10
|
urlcode benchmark --project ../my-links --requests 1000 --concurrency 2 --max-p95-ms 50
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
All three activate/validate the project with the same
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
All three activate/validate the project with the same runtime that serves it --
|
|
14
|
+
each route in its own declared trust mode, trusted in-process unless it declares
|
|
15
|
+
`sandbox: true` -- and use local environment loading like `test`. Pass an
|
|
16
|
+
external `--policy` for explicitly authorized bindings. No destination redirects are followed, credentials are not
|
|
16
17
|
printed, and no remote load-test target is accepted.
|
|
17
18
|
|
|
18
19
|
## Inventory and count reconciliation
|
package/docs/README.md
CHANGED
|
@@ -39,8 +39,8 @@ boundary and the license. Use documentation pinned to your runtime revision;
|
|
|
39
39
|
|
|
40
40
|
| Goal | Start here |
|
|
41
41
|
|---|---|
|
|
42
|
-
| Add accounts, sign-in and protected routes | [urlcode-auth](
|
|
43
|
-
| Manage users, sessions, roles and audit | [urlcode-admin](
|
|
42
|
+
| Add accounts, sign-in and protected routes | [urlcode-auth](../packages/auth#readme), [auth security](../packages/auth/SECURITY.md) |
|
|
43
|
+
| Manage users, sessions, roles and audit | [urlcode-admin](../packages/admin#readme) |
|
|
44
44
|
| Restyle every extension page and translate copy | [urlcode-ui](../packages/ui#readme), [ui contract](../packages/ui/CONTRACT.md) |
|
|
45
45
|
| Write or install a versioned extension | [Extensions](EXTENSIONS.md), [example fixture](../examples/extensions/README.md) |
|
|
46
46
|
| Follow implementation of the auth, admin and UI extensions | [Extension implementation sequence](archive/2026-09-19/EXTENSION-IMPLEMENTATION.md) |
|
|
@@ -59,7 +59,7 @@ boundary and the license. Use documentation pinned to your runtime revision;
|
|
|
59
59
|
| Review security findings and gaps | [Internal security audit](SECURITY-AUDIT.md) |
|
|
60
60
|
| Assess release readiness | [Evidence and open gates](RELEASE-READINESS.md) |
|
|
61
61
|
| See unfinished work | [Roadmap](../ROADMAP.md) |
|
|
62
|
-
| Read
|
|
62
|
+
| Read why per-route Lambda compilation was declined | [Lambda compile spike (archived)](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md) |
|
|
63
63
|
| Verify a running deployment matches the project | [Deployment checks](DEPLOYMENT-CHECKS.md) |
|
|
64
64
|
| Inspect target support | [Capabilities and normalized representation](CAPABILITIES.md) |
|
|
65
65
|
| Deploy to Vercel, AWS Lambda or Cloudflare Workers | [Vercel](VERCEL.md), [AWS](AWS.md), [Cloudflare](CLOUDFLARE.md), [provider verification evidence](PROVIDER-VERIFICATION.md) |
|
|
@@ -79,7 +79,6 @@ in the guides above and the [specification](SPECIFICATION.md).
|
|
|
79
79
|
- [Version alignment](VERSION-ALIGNMENT.md) and [release security](RELEASE-SECURITY.md)
|
|
80
80
|
describe peer compatibility and publication.
|
|
81
81
|
- Open proposals: [middleware layering](SPIKE-CORE-LAYERING.md),
|
|
82
|
-
[Lambda compilation](SPIKE-LAMBDA-COMPILE.md),
|
|
83
82
|
[agent benchmark](SPIKE-AI-FRAMEWORK-BENCHMARK.md), and
|
|
84
83
|
[business suite](SPIKE-BUSINESS-SUITE.md). None is an implementation promise.
|
|
85
84
|
- [Historical plans and reviews](archive/README.md) are archived separately.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# URLCode 0.4.1
|
|
2
|
+
|
|
3
|
+
Core, UI, auth and admin share this explicitly selected stable version. This does not enable permanent fixed versioning. Internal peer minimums advance to this version; install the coordinated set together.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install --save-exact @jimhoyd/urlcode@0.4.1 @jimhoyd/urlcode-ui@0.4.1 @jimhoyd/urlcode-auth@0.4.1 @jimhoyd/urlcode-admin@0.4.1
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
This coordinated release moves core, UI, auth and admin from `0.4.0-alpha.3` to stable `0.4.1`. It makes the reviewed monorepo release line available through npm `latest` and keeps the four packages' peer minimums aligned.
|
|
10
|
+
|
|
11
|
+
The runtime retains its existing trust model: project functions and middleware run trusted in Node by default; routes declaring `sandbox: true` retain QuickJS/WASM isolation. The stable label is a distribution decision, not an independent security assessment or hostile multi-tenant readiness claim.
|
|
12
|
+
|
|
13
|
+
Release preparation now supports an explicit exit from alpha. Publication promotes the exact signed candidate archives, pins their manifest digest in immutable tags, checks actual npm installability, and updates the standalone starter to the published core version. Historical alpha versions and tags remain unchanged.
|
|
14
|
+
|
|
15
|
+
### admin-kit-only-console.md
|
|
16
|
+
|
|
17
|
+
**Breaking:** the console renders only through the urlcode-ui kit. `ui` is now a
|
|
18
|
+
required option of `adminExtension` and of `createAdministrationRuntime`'s
|
|
19
|
+
`admin` block, which is itself no longer optional.
|
|
20
|
+
|
|
21
|
+
`@jimhoyd/urlcode-ui` was already a required peer dependency, so nothing new has
|
|
22
|
+
to be installed. What changed is that the `ui` *extension* must now be supplied
|
|
23
|
+
and active: the primitive render path — the same `admin/*` templates rendered
|
|
24
|
+
through the shared primitives inside a console shell admin built itself — is
|
|
25
|
+
gone, along with the `RenderPath` seam, the `activeKit()` helper, the
|
|
26
|
+
`ScreenOptions.shell.sidebar` markup and `src/admin-presentation.ts`. The kit
|
|
27
|
+
builds the sidebar, page header and skip target from the `nav` items and account
|
|
28
|
+
`menu` admin supplies, so the console shell has one representation instead of
|
|
29
|
+
two. `ScreenOptions.preferences` is gone too: the kit layout now renders through
|
|
30
|
+
the same resolved presentation as the body, so the document's `lang` matches the
|
|
31
|
+
copy on the page.
|
|
32
|
+
|
|
33
|
+
Activation refuses up front, rather than failing per request in production, when
|
|
34
|
+
|
|
35
|
+
- `ui` is missing,
|
|
36
|
+
- `ui` is supplied but not active yet — declare `ui` before `admin` under
|
|
37
|
+
`extensions` in `urlcode.yaml`, since the runtime activates in declaration
|
|
38
|
+
order, and mount its assets route, or
|
|
39
|
+
- the kit was built without `adminUiTemplates`.
|
|
40
|
+
|
|
41
|
+
To migrate, build the extension with admin's templates and pass it:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
const ui = createUiExtension({projectSha256, projectRoot, sources: [authCatalogue], extensions: [authUiTemplates, adminUiTemplates]});
|
|
45
|
+
adminExtension({service, csrfKey, projectSha256, ui});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Scaffolding emits that wiring for you: `scaffold()`, `initAdministration` and
|
|
49
|
+
`urlcode init --with ui,auth,admin` compose the kit and register the admin
|
|
50
|
+
templates with it. `ui` is now required, and must come before `admin`; the
|
|
51
|
+
scaffold refuses otherwise before writing anything.
|
|
52
|
+
|
|
53
|
+
### auth-kit-only-render.md
|
|
54
|
+
|
|
55
|
+
Breaking: the `ui` extension is now required. Every account screen renders through the `urlcode-ui` kit; the shared-primitive fallback is gone. `authExtension({ui, ...})` refuses activation when `ui` is absent or when the runtime has not activated it, naming the missing piece instead of failing per request. Declare `ui` before `auth` in `urlcode.yaml` (with its asset route) and list `ui.registration` before `authExtension` in the host: the runtime activates extensions in the order `urlcode.yaml` declares them. `@jimhoyd/urlcode-ui` was already a required peer dependency, so nothing new needs installing; what changes is that the extension must be supplied and active. `ScreenOptions.ui` is no longer optional and `screenObserver` no longer reports a render path.
|
|
56
|
+
|
|
57
|
+
Scaffolding composes the kit for you: `urlcode init --with ui,auth` and the standalone `initAuthentication` now write a project whose `urlcode.yaml` declares `ui` first and whose host passes it to `authExtension`. The scaffold refuses when `ui` is missing, or ordered after `auth`, before anything is written.
|
|
58
|
+
|
|
59
|
+
### auth-store-startup-diagnostics.md
|
|
60
|
+
|
|
61
|
+
Report which startup phase an auth store worker reached when its 15-second bound elapses, and reject at once when the worker fails or exits before reporting readiness instead of waiting the bound out. The status and code are unchanged; the detail is attached as the error's cause for operator logs and never reaches a response.
|
|
62
|
+
|
|
63
|
+
### pin-generated-site-dependencies.md
|
|
64
|
+
|
|
65
|
+
`urlcode-auth init` now writes a `package.json` that pins this package and each declared peer at the exact version installed beside it, instead of a manifest with no dependencies at all; `initAuthentication` returns those pins and names any peer it could not resolve. Nothing is installed: running `npm install` in the generated directory to produce a lockfile stays the operator's explicit step, and no upgrade command exists.
|
|
66
|
+
|
|
67
|
+
### ui-scaffold-peer-registration.md
|
|
68
|
+
|
|
69
|
+
The scaffold wires kit-rendering peers into the host it generates. `scaffold()` reads the composed `names` and emits `createUiExtension({..., sources: [authCatalogue], extensions: [authUiTemplates, adminUiTemplates]})`, importing each peer it needs, so `urlcode init --with ui,auth,admin` produces a project that activates. Previously it always wrote `sources: []` and no `extensions`, which left auth and admin without their copy and templates. `ui` alone still registers nothing and imports no peer.
|
|
70
|
+
|
|
71
|
+
Name `ui` first: the runtime activates extensions in the order `urlcode.yaml` declares them, core writes that file in `--with` order, and auth and admin both refuse to activate before the kit is active.
|
|
72
|
+
|
|
73
|
+
Publish to the npm `latest` channel in core → UI → auth → admin order after exact-commit CI and candidate verification. Existing tags and the `alpha` channel stay unchanged; this stable release advances `latest`. Changesets prerelease mode is exited. Update the standalone starter's exact core pin after registry installability is verified. This preparation is not evidence of publication or an independent security assessment.
|
package/docs/RELEASE-SECURITY.md
CHANGED
|
@@ -18,16 +18,18 @@ means a successful nightly or manual `ci.yml` run across every supported OS/Node
|
|
|
18
18
|
combination; the compact push/PR matrix alone does not authorize publication. Package manifests,
|
|
19
19
|
lockfile versions and tag names must agree. Published peer floors must exist and
|
|
20
20
|
extension tests must resolve the published packages, not workspace source.
|
|
21
|
-
The
|
|
22
|
-
|
|
21
|
+
The candidate builds all package archives once using the digest-pinned Node
|
|
22
|
+
image from Dockerfile; publishers promote those exact signed bytes. The builder
|
|
23
|
+
installs Git from Debian for repository-fixture tests; Git is a test dependency,
|
|
24
|
+
not an addition to the runtime image or npm package. Locked dependencies,
|
|
23
25
|
verification, runtime audit, package installation tests and local drills precede
|
|
24
26
|
packing. Build commands in that container receive no GitHub token.
|
|
25
27
|
|
|
26
28
|
The candidate workflow stores artifacts without publishing. A tagged release
|
|
27
29
|
publishes npm only when `PUBLISH_NPM=true` and core images only when
|
|
28
|
-
`PUBLISH_CONTAINER=true`.
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
`PUBLISH_CONTAINER=true`. Candidate and release artifacts are retained for 90 days, and each package
|
|
31
|
+
GitHub release stores the complete signed candidate bundle. Retries verify and
|
|
32
|
+
reuse original retained or durable bytes; missing originals fail closed. Keep independent last-good artifacts and
|
|
31
33
|
rehearse deployment rollback; Actions retention is not an archival guarantee.
|
|
32
34
|
|
|
33
35
|
## Identity and provenance
|
|
@@ -39,9 +41,12 @@ requires a reviewed registry trust migration. Successful preparation does not
|
|
|
39
41
|
prove the registry-side identity permits direct publication.
|
|
40
42
|
|
|
41
43
|
`actions/attest` signs the candidate files with GitHub OIDC/Sigstore provenance.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
The candidate bundle includes all four archives, dependency SBOM, build manifest,
|
|
45
|
+
train metadata, checksums and Homebrew formula. Each publisher retains that
|
|
46
|
+
bundle and publishes only its selected npm archive. New annotated version tags
|
|
47
|
+
pin the candidate run ID and signed-manifest SHA256. The run ID is also bound
|
|
48
|
+
into the manifest; the digest prevents another attempt of that run from
|
|
49
|
+
substituting different artifacts. The core manifest records source SHA, lockfile hash, Node and
|
|
45
50
|
TypeScript versions and emitted-file hashes. `dist/` is built, never committed.
|
|
46
51
|
|
|
47
52
|
Verify an artifact with `gh attestation verify <tarball> --repo
|
|
@@ -59,14 +64,18 @@ identical SHA-512 integrity. GitHub assets are compared and missing assets added
|
|
|
59
64
|
existing unequal assets are never clobbered. Transient registry errors fail
|
|
60
65
|
closed rather than count as an unpublished version.
|
|
61
66
|
|
|
62
|
-
Alpha versions use npm/GHCR `alpha` and GitHub prerelease classification.
|
|
63
|
-
|
|
67
|
+
Alpha versions use npm/GHCR `alpha` and GitHub prerelease classification.
|
|
68
|
+
Explicit stable versions use npm `latest` and normal GitHub releases. Only the
|
|
69
|
+
stable core release advances GitHub `latest`, so the core installer cannot pick
|
|
70
|
+
an extension release. Stable preparation removes Changesets prerelease mode;
|
|
71
|
+
existing alpha artifacts, tags and channel pointers remain unchanged. Mutable npm
|
|
64
72
|
and container channels cannot regress to an older version. Existing core image
|
|
65
73
|
versions are reused only with matching source labels; unlabeled historical
|
|
66
74
|
images require a reviewed migration rather than an inferred identity.
|
|
67
75
|
|
|
68
|
-
The coordinator
|
|
69
|
-
|
|
76
|
+
The coordinator verifies candidate availability and provenance before creating
|
|
77
|
+
any version tag, then creates one at a time and waits for successful publication
|
|
78
|
+
and consumer-facing registry installability before releasing dependents. Shared publication concurrency avoids cross-version races.
|
|
70
79
|
The active immutable-tag rule blocks release tag updates/deletions with no
|
|
71
80
|
bypass actors; its configuration is in `.github/rulesets/release-tags.json`. No automation needs permission to bypass main checks or approve
|
|
72
81
|
its own PR. A repository-scoped GitHub App is the preferred eventual automation
|
|
@@ -79,3 +88,9 @@ publish path, OIDC trust for every package, tag protection, GHCR behavior and
|
|
|
79
88
|
partial-failure recovery must be verified on an authorized release; local tests
|
|
80
89
|
and workflow inspection cannot prove them. The release helpers do not alter
|
|
81
90
|
historical npm channels, GitHub flags, tags or registry artifacts.
|
|
91
|
+
|
|
92
|
+
The coordinated `.3` release proved the existing OIDC identities, but exposed
|
|
93
|
+
missing-artifact retry behavior tracked in #223. New workflows fail closed or
|
|
94
|
+
recover verified durable bytes; old immutable tags retain their original
|
|
95
|
+
workflow code. The new promotion path still needs a future explicitly authorized
|
|
96
|
+
release rehearsal; implementing it does not publish a new version.
|
package/docs/SPECIFICATION.md
CHANGED
|
@@ -202,7 +202,11 @@ entry may likewise be a string, normalized to `{source: <string>}`. Only the
|
|
|
202
202
|
long form exists after loading, so `routes`, `audit`, `explain`, revision hashes
|
|
203
203
|
and the field reference describe the expansion.
|
|
204
204
|
|
|
205
|
-
ES modules only
|
|
205
|
+
ES modules only. `.mjs` is loaded as ESM in both modes. A `sandbox: true` route
|
|
206
|
+
also reads `.js` as ESM independently of Node package settings, but a trusted
|
|
207
|
+
route imports `.js` through Node's own loader, so an ESM `.js` module needs the
|
|
208
|
+
nearest `package.json` to declare `"type": "module"`; under `"type": "commonjs"`
|
|
209
|
+
it fails activation. Prefer `.mjs` for modules that must work in either mode.
|
|
206
210
|
[Build-time TypeScript authoring](TYPESCRIPT-AUTHORING.md) can produce these
|
|
207
211
|
JavaScript modules in a separate output project; serving does not transpile them.
|
|
208
212
|
The build never imports application code into Node, uses fixed compiler settings,
|
|
@@ -35,7 +35,7 @@ either is blocked on the other.
|
|
|
35
35
|
Core's job is to stay the smallest thing that is still a complete product on
|
|
36
36
|
its own: YAML routing (`redirect`/`respond`/`page`/`static`/`download`) plus
|
|
37
37
|
`function`, the one primitive with an actual serverless story
|
|
38
|
-
([`SPIKE-LAMBDA-COMPILE.md`](SPIKE-LAMBDA-COMPILE.md) — compile each `function`
|
|
38
|
+
([`SPIKE-LAMBDA-COMPILE.md`](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md) — compile each `function`
|
|
39
39
|
route to its own Lambda, the pattern `src/build-cloudflare.ts` already uses).
|
|
40
40
|
That base alone serves static and dynamic content and deploys anywhere
|
|
41
41
|
(`docs/FRAMEWORK.md` layer 1–3). Everything past that — accounts, admin,
|
package/docs/STARTERS.md
CHANGED
|
@@ -36,18 +36,30 @@ on every push and pull request once the project is on GitHub.
|
|
|
36
36
|
See [organization](ORGANIZATION.md) for choosing your own layout. Defaults allow
|
|
37
37
|
GET/HEAD and use redirect 302; add configuration only when changing behavior.
|
|
38
38
|
|
|
39
|
+
`urlcode init` writes no `package.json`: the route project is route-only, and its
|
|
40
|
+
runtime may be installed globally, in a parent workspace or in a container. Add
|
|
41
|
+
`--manifest` to also write one pinning the runtime at exactly the version that
|
|
42
|
+
generated the project, then run `npm install` in it yourself to install that
|
|
43
|
+
version and produce a lockfile. The CLI never runs a package manager, and no
|
|
44
|
+
upgrade command exists — a pinned version changes when you edit the manifest.
|
|
45
|
+
|
|
39
46
|
Initialization refuses an existing destination. Own the app in your own repository,
|
|
40
47
|
keep secrets out of Git, and upgrade the runtime separately without regenerating
|
|
41
48
|
application files. Add pages, downloads, more functions and business-specific
|
|
42
49
|
features to this same project. Update tests and the expected route count as it grows.
|
|
43
50
|
See [readiness](READINESS.md) and [security](FUNCTION-SECURITY.md).
|
|
44
51
|
|
|
45
|
-
To start an extended site instead, install the
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
To start an extended site instead, install the compatible core, UI, auth and
|
|
53
|
+
admin set from npm in the directory you run from; the
|
|
54
|
+
[framework guide](FRAMEWORK.md#the-composition-contract) provides the exact
|
|
55
|
+
`0.4.1` command to use after publication. Pass UI first so its kit activates
|
|
56
|
+
before auth: `urlcode init ../my-site --with ui,auth,admin` writes the same starter under `my-site/app/`, merges each package's
|
|
49
57
|
routes and declarations into it, and generates one `host.mjs` and README beside
|
|
50
|
-
it.
|
|
58
|
+
it, plus a `package.json` pinning the runtime, those packages and their declared
|
|
59
|
+
peers at the versions it just resolved, validated together against every declared
|
|
60
|
+
peer range. Installing them is your explicit `npm install` in that directory.
|
|
61
|
+
The contract each package fulfils is in [extensions](EXTENSIONS.md#scaffolding-with-init---with),
|
|
62
|
+
with `--no-manifest` and `--pin` in [recorded versions](EXTENSIONS.md#recorded-versions).
|
|
51
63
|
|
|
52
64
|
Both paths carry an `AGENTS.md` for repository-aware assistants. `urlcode init`
|
|
53
65
|
generates it from the installed runtime's capability catalog (the same source as
|
package/docs/TOOLING.md
CHANGED
|
@@ -81,10 +81,12 @@ network. Keys always appear in this order:
|
|
|
81
81
|
`--host-file`, `host` counts the operator module's extensions and plugins
|
|
82
82
|
without activating them.
|
|
83
83
|
- `routes`: path, methods and handler per route, sorted by path.
|
|
84
|
-
- `constraints`: a fixed list that holds for every project (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
- `constraints`: a fixed list that holds for every project (network and Node
|
|
85
|
+
built-ins available to trusted code and withdrawn by `sandbox: true`, no
|
|
86
|
+
regex routes, one handler per route, exact or `{param}` path segments,
|
|
87
|
+
subtree mounts only for static and extension routes, no YAML interpolation,
|
|
88
|
+
injected `env`/`secrets` by operator grant only), each with a value and a
|
|
89
|
+
note spelling out how it differs between the two trust modes.
|
|
88
90
|
- `targets`: for each capability target (or the one `--target`), which of this
|
|
89
91
|
project's used features are supported, conditional, refused or unknown.
|
|
90
92
|
- `commands`: the exact `validate`, `test`, `audit --expect-routes N` (N is
|
package/docs/VERCEL.md
CHANGED
|
@@ -7,7 +7,8 @@ that is the point of the project format.
|
|
|
7
7
|
**This adapter serves native handlers only:** redirects, validated responses,
|
|
8
8
|
pages, static assets and downloads. `function` and `middleware` routes are
|
|
9
9
|
refused at activation, trusted or sandboxed alike, not per request, so a deployment cannot
|
|
10
|
-
half-work
|
|
10
|
+
half-work, and that is a settled position rather than a pending limitation. See
|
|
11
|
+
[what is not supported](#what-this-adapter-does-not-do).
|
|
11
12
|
|
|
12
13
|
A working project is in [`examples/vercel/`](../examples/vercel/).
|
|
13
14
|
|
|
@@ -71,13 +72,20 @@ and want it in generated URLs.
|
|
|
71
72
|
|
|
72
73
|
| Not supported | Why |
|
|
73
74
|
|---|---|
|
|
74
|
-
| `function` routes | They need the self-hosted Node lifecycle; a `sandbox: true` route would additionally spawn worker threads and load the WASM engine on every cold start. Correctness is not the issue;
|
|
75
|
+
| `function` routes | They need the self-hosted Node lifecycle; a `sandbox: true` route would additionally spawn worker threads and load the WASM engine on every cold start. Correctness is not the issue; the execution model is — per-route compilation was considered and declined. |
|
|
75
76
|
| Middleware | Runs in the same execution mode as the route's function, and is refused with it. |
|
|
76
77
|
| `urlcode serve` operational endpoints | `/_urlcode/health` and `/_urlcode/ready` describe a long-lived process. Use Vercel's own observability. |
|
|
77
78
|
|
|
78
79
|
Each refusal happens at activation with a message naming the route, so you find
|
|
79
80
|
out on deploy rather than on a request.
|
|
80
81
|
|
|
82
|
+
`function` and `middleware` are not coming to this adapter. The supported answer
|
|
83
|
+
is to deploy the project as one trusted Node process — a container or a VM
|
|
84
|
+
running the project as it runs locally — which supports every route type today,
|
|
85
|
+
on any host you like including AWS (ECS, EC2, App Runner). See
|
|
86
|
+
[the decision](OPEN-DECISIONS.md#accepted-one-node-deployment-per-project) and
|
|
87
|
+
[the analysis behind it](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md).
|
|
88
|
+
|
|
81
89
|
## Operating it
|
|
82
90
|
|
|
83
91
|
Every instance activates the project independently: parsing YAML, snapshotting
|
|
@@ -26,18 +26,52 @@ must match its manifest. `npm run release:check` rejects stale lockfile versions
|
|
|
26
26
|
Unreleased source changes do not require moving a published tag or pretending a
|
|
27
27
|
new package has already shipped.
|
|
28
28
|
|
|
29
|
+
The `0.4.1` release is an explicit stable release decision for core, UI, auth
|
|
30
|
+
and admin. Publication moves each package's npm `latest` channel to `0.4.1`, in
|
|
31
|
+
core → UI → auth → admin order, after its release checks pass. A prepared
|
|
32
|
+
manifest or merged release PR does not prove registry publication: use
|
|
33
|
+
`npm run release:status` to inspect the live result before installing the set.
|
|
34
|
+
This alignment does not permanently couple package versions; subsequent
|
|
35
|
+
releases can still select only the packages that changed.
|
|
36
|
+
|
|
29
37
|
Alpha releases publish under `alpha`; they never automatically move npm
|
|
30
|
-
`latest`.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
`
|
|
36
|
-
|
|
38
|
+
`latest`. Stable publication does not move `alpha`, so the two channels can
|
|
39
|
+
legitimately show different versions. Test the install combination you recommend
|
|
40
|
+
against peer ranges. `release:status` reports each declared peer floor and
|
|
41
|
+
whether its current `latest` and `alpha` satisfy the range.
|
|
42
|
+
|
|
43
|
+
After all four `0.4.1` versions are published, install the aligned set with:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npm install --save-exact @jimhoyd/urlcode@0.4.1 @jimhoyd/urlcode-ui@0.4.1 @jimhoyd/urlcode-auth@0.4.1 @jimhoyd/urlcode-admin@0.4.1
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Bare package names resolve npm's current `latest`; exact application pins and a
|
|
50
|
+
committed lockfile keep an existing application from changing on a new release.
|
|
51
|
+
|
|
52
|
+
## Generated applications
|
|
53
|
+
|
|
54
|
+
A generated application records its own versions. `urlcode init --with` writes a
|
|
55
|
+
`package.json` pinning the running runtime, the named extensions and their
|
|
56
|
+
declared peers at the exact versions resolved at generation time, after checking
|
|
57
|
+
that set against every declared peer range; `urlcode init --manifest` does the
|
|
58
|
+
same for a route-only project with the runtime alone; `urlcode-auth init` pins
|
|
59
|
+
this package and its peers. Plain `urlcode init` stays route-only and writes no
|
|
60
|
+
manifest, for projects whose runtime is managed elsewhere. Generation never runs
|
|
61
|
+
a package manager: `package-lock.json` exists only after the operator runs
|
|
62
|
+
`npm install` in the generated directory, and a pin taken from a local path or
|
|
63
|
+
tarball reproduces only where that path exists.
|
|
64
|
+
|
|
65
|
+
No upgrade command exists. A generated project moves to new versions by an
|
|
66
|
+
operator editing its manifest and re-installing. The issue that asked for this
|
|
67
|
+
(#212) describes a future command that would choose a tested compatible set,
|
|
68
|
+
show the changes and require explicit alpha selection; nothing here implements
|
|
69
|
+
that, and the pins above are only the groundwork it would need.
|
|
37
70
|
|
|
38
71
|
The standalone `urlcode-template` is an external exact-version consumer: after a
|
|
39
72
|
runtime release, update its dependency and starter through its own reviewed PR.
|
|
40
|
-
|
|
73
|
+
The coordinator prepares and checks that PR after registry installation succeeds;
|
|
74
|
+
the template is a consumer update, not a fifth npm package. The retired
|
|
41
75
|
`urlcode-docs`, `urlcode-middleware`, `urlcode-dynamic-link` and `urlcode-short`
|
|
42
76
|
repositories are not release targets.
|
|
43
77
|
|
|
@@ -6,6 +6,7 @@ current instructions. See the [current roadmap](../../../ROADMAP.md),
|
|
|
6
6
|
Remaining acceptance work is not declared complete by archiving this record.
|
|
7
7
|
|
|
8
8
|
<!-- trust-model-prose: historical-file -->
|
|
9
|
+
<!-- local-links: historical-file -->
|
|
9
10
|
<!-- guidance-claims: ignore-file -->
|
|
10
11
|
|
|
11
12
|
# Public roadmap
|
|
@@ -6,6 +6,7 @@ current instructions. See the [current roadmap](../../../ROADMAP.md),
|
|
|
6
6
|
Remaining acceptance work is not declared complete by archiving this record.
|
|
7
7
|
|
|
8
8
|
<!-- trust-model-prose: historical-file -->
|
|
9
|
+
<!-- local-links: historical-file -->
|
|
9
10
|
<!-- guidance-claims: ignore-file -->
|
|
10
11
|
|
|
11
12
|
# Review: the extension model, its precedents and its alignment
|