@jimhoyd/urlcode 0.4.1 → 0.4.2
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 +10 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/CONTRIBUTING.md +36 -0
- package/ROADMAP.md +16 -12
- package/dist/BUILD-MANIFEST.json +6 -6
- package/dist/cli.js +2 -1
- package/dist/extensions.js +78 -1
- package/dist/mcp.js +1 -1
- package/dist/policy.js +16 -0
- package/dist/readiness.js +5 -1
- package/dist/tooling.js +2 -1
- package/dist/types/extensions.d.ts +58 -0
- package/dist/types/readiness.d.ts +3 -0
- package/dist/types/tooling.d.ts +1 -0
- package/docs/AI-AUTHORING.md +5 -4
- package/docs/COMPOSING-A-SITE.md +15 -6
- package/docs/CONTAINER-PROMOTION.md +74 -0
- package/docs/DEVELOPMENT-PIPELINE.md +39 -5
- package/docs/EXTENSIONS.md +53 -88
- package/docs/FUNCTION-SECURITY.md +5 -8
- package/docs/OPEN-DECISIONS.md +27 -102
- package/docs/READINESS.md +4 -1
- package/docs/README.md +16 -10
- package/docs/RELEASE-0.4.2.md +30 -0
- package/docs/RELEASE-READINESS.md +40 -11
- package/docs/RELEASE-SECURITY.md +6 -2
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +9 -13
- package/docs/TOOLING.md +1 -1
- package/docs/VERSION-ALIGNMENT.md +8 -0
- package/docs/archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md +116 -0
- package/docs/archive/README.md +1 -0
- package/llms-full.txt +79 -107
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/packaging/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +10 -0
- package/skills/urlcode/SKILL.md +8 -1
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# Release readiness
|
|
2
2
|
|
|
3
|
-
Status:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
the workload and
|
|
10
|
-
|
|
3
|
+
Status: this register records evidence and open gates for the public runtime.
|
|
4
|
+
It deliberately carries no current version table: the manifests are the version
|
|
5
|
+
authority, [VERSION-ALIGNMENT.md](VERSION-ALIGNMENT.md) explains channels and
|
|
6
|
+
ownership, and `npm run release:status` reads live registry and tag state. A
|
|
7
|
+
stable release is a packaging fact. It is not production approval and does not
|
|
8
|
+
perform any gate under "Gates before production approval" below; neither does a
|
|
9
|
+
passing CI run. Production approval remains specific to the workload and
|
|
10
|
+
deployment environment. Registry channels and deployments were not re-checked
|
|
11
|
+
when this register was reconciled on 2026-09-20 (#242).
|
|
11
12
|
Use the contract and docs from the same pinned commit as your installed runtime.
|
|
12
13
|
|
|
13
14
|
## What is aligned
|
|
@@ -41,7 +42,34 @@ deferred to the post-merge run.
|
|
|
41
42
|
| Worker replacement | Repeated guest deadlines shed load and the pool returns to service after backoff, rather than latching off for the life of the process | Bounded by the configured worker count; no cross-process load balancing |
|
|
42
43
|
| Shutdown | New work rejects; repeated close shares completion | Existing deadlines can still fail during shutdown |
|
|
43
44
|
| Activation/recovery | Invalid reload retains last-good snapshot; corrupt revision metadata rejects activation | No deployment orchestration |
|
|
44
|
-
| Packaging | Packed installation and starter examples tested; sensitive files excluded |
|
|
45
|
+
| Packaging | Packed installation and starter examples tested; sensitive files excluded | Published versions and channels: see VERSION-ALIGNMENT.md and `release:status`; this row asserts none. Dated registry observations are kept under "Dated packaging observations" below. GitHub Releases attach a Homebrew formula (`urlcode.rb`) for manual copy into a tap, not an automated Homebrew Core/tap publish. No provider adapter guarantee. |
|
|
46
|
+
|
|
47
|
+
## Dated packaging observations
|
|
48
|
+
|
|
49
|
+
Kept as observed; each is true only of its date and none is current status.
|
|
50
|
+
Use `npm run release:status` for the present.
|
|
51
|
+
|
|
52
|
+
- **2026-09-19, registry check.** `@jimhoyd/urlcode` had `latest` = `0.3.0` and
|
|
53
|
+
`alpha` = `0.4.0-alpha.1`; the repository then stood at `0.4.0-alpha.2`, which
|
|
54
|
+
was unpublished. Published extension packages that day:
|
|
55
|
+
`@jimhoyd/urlcode-auth@0.1.0-alpha.2`, `@jimhoyd/urlcode-admin@0.1.0-alpha.2`,
|
|
56
|
+
`@jimhoyd/urlcode-ui@0.1.0-alpha.4`.
|
|
57
|
+
- **2026-09-19, retired packages.** `@jimhoyd/urlcode-short@0.1.0-alpha.1`,
|
|
58
|
+
`@jimhoyd/urlcode-dynamic-link@0.1.0-alpha.1` and
|
|
59
|
+
`@jimhoyd/urlcode-middleware@0.1.0-alpha.2` were published that day, then
|
|
60
|
+
retired and unpublished later the same day; all three repositories were
|
|
61
|
+
deleted. Middleware's withdrawal removed no capability: per-route middleware
|
|
62
|
+
is native to core.
|
|
63
|
+
- **2026-09-19, observed in passing.** auth's dist-tags were split (`alpha` at
|
|
64
|
+
`0.1.0-alpha.2`, `latest` at `0.1.0-alpha.1`), so a plain install resolved a
|
|
65
|
+
build below admin's declared floor.
|
|
66
|
+
- **Later 2026-09-19 or after (the source did not date these; treat as
|
|
67
|
+
superseded).** auth and admin both read `latest` = `alpha` =
|
|
68
|
+
`0.1.0-alpha.3`, resolving the split above; `@jimhoyd/urlcode-ui` carried a
|
|
69
|
+
deliberate `alpha` = `0.1.0-alpha.6` / `latest` = `0.1.0-alpha.5` split, safe
|
|
70
|
+
only because admin's ui floor was exactly `>=0.1.0-alpha.5`. The stable
|
|
71
|
+
`0.4.1` alignment described in [VERSION-ALIGNMENT.md](VERSION-ALIGNMENT.md)
|
|
72
|
+
postdates these.
|
|
45
73
|
|
|
46
74
|
`npm run check:downstream-skills` is a manual, advisory report worth running
|
|
47
75
|
before a release: it diffs core's `.claude/skills/` copies against copies
|
|
@@ -80,7 +108,8 @@ benchmark into a universal throughput claim.
|
|
|
80
108
|
extension needs its own backup/restore drill; core has no durable store.
|
|
81
109
|
4. Alerting and ownership for sustained errors, latency, readiness, dropped logs,
|
|
82
110
|
disk space, restarts and backups. Pick service objectives for the actual app.
|
|
83
|
-
5. Stable-release support commitments before packaging/public reuse claims
|
|
111
|
+
5. Stable-release support commitments before packaging/public reuse claims;
|
|
112
|
+
a stable release exists, but that does not by itself record such commitments.
|
|
84
113
|
Private security reporting and the current support baseline are documented in
|
|
85
114
|
SECURITY.md.
|
|
86
115
|
|
|
@@ -88,7 +117,7 @@ License selection is resolved: URLCode is released under the Apache License 2.0,
|
|
|
88
117
|
`package.json` declares it, and the repository carries the full license text.
|
|
89
118
|
The remaining gates above are engineering and operational, not legal.
|
|
90
119
|
|
|
91
|
-
The
|
|
120
|
+
The next-phase source (shipped in the alpha releases; see the archived plan) includes strict bulk/provider interchange,
|
|
92
121
|
local recipes, TypeScript authoring, bounded self-hosted proxy/signals and read-only
|
|
93
122
|
MCP. Local AWS/Vercel/Cloudflare adapter tests and deployment probe tooling exist,
|
|
94
123
|
but actual provider deployments remain unverified. Node process/container hosting
|
package/docs/RELEASE-SECURITY.md
CHANGED
|
@@ -78,8 +78,12 @@ any version tag, then creates one at a time and waits for successful publication
|
|
|
78
78
|
and consumer-facing registry installability before releasing dependents. Shared publication concurrency avoids cross-version races.
|
|
79
79
|
The active immutable-tag rule blocks release tag updates/deletions with no
|
|
80
80
|
bypass actors; its configuration is in `.github/rulesets/release-tags.json`. No automation needs permission to bypass main checks or approve
|
|
81
|
-
its own PR.
|
|
82
|
-
|
|
81
|
+
its own PR. Manual package/all release workflows share the coordinator and need
|
|
82
|
+
`RELEASE_AUTOMATION_TOKEN` because events created by the ordinary `GITHUB_TOKEN`
|
|
83
|
+
do not trigger tag publishers. A repository-scoped GitHub App is preferred. A
|
|
84
|
+
fine-grained PAT may select only `urlcode` and `urlcode-template`, with Contents,
|
|
85
|
+
Pull requests and Actions read/write plus Checks read. Neither identity needs
|
|
86
|
+
ruleset bypass, administration, npm secrets or review approval.
|
|
83
87
|
|
|
84
88
|
## Remaining validation
|
|
85
89
|
|
|
@@ -174,19 +174,15 @@ with `add_repo` before treating it as settled.
|
|
|
174
174
|
settles this explicitly: a hook a project names in an extension's own YAML
|
|
175
175
|
config (`urlcode-auth`'s `onSignUp`/`beforeRegister`/`onDelete` and the
|
|
176
176
|
like) is first-party project code in the same category as any
|
|
177
|
-
`function`/`middleware` route
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
[FUNCTION-SECURITY.md](FUNCTION-SECURITY.md)
|
|
186
|
-
closes that: the same worker/QuickJS engine `FunctionPool` already used for
|
|
187
|
-
route dispatch, generalized to explicit `{source, export}` entries/targets
|
|
188
|
-
instead of `FunctionRoute`, with no second engine and no "trusted" mode
|
|
189
|
-
exported alongside it.
|
|
177
|
+
`function`/`middleware` route and runs trusted and in-process. Core now owns
|
|
178
|
+
the shared extension-hook primitive: it validates hook references, imports
|
|
179
|
+
them during activation, enforces the extension-published input/output JSON
|
|
180
|
+
Schemas, reports those contracts to authoring tools, and includes hook entry
|
|
181
|
+
files in the project revision. Hook sandboxing is not part of this first
|
|
182
|
+
contract; `sandbox: true` is rejected during activation instead of silently
|
|
183
|
+
running trusted. A later sandbox contract must define its serialization and
|
|
184
|
+
capability boundary before it can reuse the worker/QuickJS path described in
|
|
185
|
+
[FUNCTION-SECURITY.md](FUNCTION-SECURITY.md).
|
|
190
186
|
|
|
191
187
|
## Recommended sequencing
|
|
192
188
|
|
package/docs/TOOLING.md
CHANGED
|
@@ -52,7 +52,7 @@ The tooling API consolidates authoring operations without starting a runtime:
|
|
|
52
52
|
- `listRecipes()` and `showRecipe(name)` expose the fixed bundled recipe catalog.
|
|
53
53
|
- `inspectExtensions({project, hostFile?})` reports each operator-registered
|
|
54
54
|
extension's name, contract version, targets, credential headers, configuration
|
|
55
|
-
and policy JSON Schemas, whether the project declares it, whether its revision
|
|
55
|
+
and policy JSON Schemas, machine-readable project hook contracts, whether the project declares it, whether its revision
|
|
56
56
|
pin matches and where routes mount or require it, plus the project's declared
|
|
57
57
|
names. With `hostFile` it executes that trusted operator module under the
|
|
58
58
|
`--host-file` rules (absolute path, outside the project) and releases it
|
|
@@ -26,6 +26,14 @@ 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
|
+
Manual GitHub Actions releases can select `core`, `ui`, `auth`, `admin`, or
|
|
30
|
+
`all`. A single-package release updates only that package's manifest, lock entry,
|
|
31
|
+
changelog and relevant Changesets; core also owns its duplicated CLI/MCP/plugin
|
|
32
|
+
version metadata and downstream starter update. The all-packages action aligns
|
|
33
|
+
every manifest and advances internal peer floors together. Changesets that name
|
|
34
|
+
packages across the selected boundary must be released together rather than
|
|
35
|
+
partially consumed.
|
|
36
|
+
|
|
29
37
|
The `0.4.1` release is an explicit stable release decision for core, UI, auth
|
|
30
38
|
and admin. Publication moves each package's npm `latest` channel to `0.4.1`, in
|
|
31
39
|
core → UI → auth → admin order, after its release checks pass. A prepared
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Historical record
|
|
2
|
+
|
|
3
|
+
Archived 2026-09-20 from [open decisions](../../OPEN-DECISIONS.md), which was
|
|
4
|
+
reconciled against its issues that day (#242). These sections record completed
|
|
5
|
+
work and dated observations exactly as they stood on 2026-09-19 (core `db375bf`
|
|
6
|
+
and the package sources of that date); they are not current instructions and
|
|
7
|
+
their version numbers, dist-tags, commits and pins are not current. Versions
|
|
8
|
+
and channels: [version alignment](../../VERSION-ALIGNMENT.md) and
|
|
9
|
+
`npm run release:status`. Archiving does not declare any security, deployment,
|
|
10
|
+
recovery or provider check performed. The live middleware decision remains in
|
|
11
|
+
open decisions.
|
|
12
|
+
|
|
13
|
+
<!-- trust-model-prose: historical-file -->
|
|
14
|
+
<!-- guidance-claims: ignore-file -->
|
|
15
|
+
<!-- local-links: historical-file -->
|
|
16
|
+
|
|
17
|
+
## Closed questions removed from the active list
|
|
18
|
+
|
|
19
|
+
- Publishing convention is recorded in [version alignment](../../VERSION-ALIGNMENT.md):
|
|
20
|
+
publishable manifests on main, releases through reviewed tags/workflows.
|
|
21
|
+
- Core `0.4.0-alpha.2` and current extension releases exist; publishing that
|
|
22
|
+
already-shipped version is not a next step.
|
|
23
|
+
- Auth/admin kit adoption and shared form helpers are implemented in their code.
|
|
24
|
+
- The UI primitive fallback is retired, which settles the question this table
|
|
25
|
+
carried. Auth and admin now render every screen through the kit and refuse
|
|
26
|
+
activation without it (`packages/auth/src/auth.ts`: "there is no
|
|
27
|
+
shared-primitive fallback"), so "keep both" no longer describes the code.
|
|
28
|
+
- The template pins `0.4.0-alpha.2`. Its skill differences were read against that
|
|
29
|
+
pin: omitted handlers and advice about the removed management API are stale,
|
|
30
|
+
not intentional older-version behavior.
|
|
31
|
+
- The guidance checks run through `npm run check` inside `verify`; a regex check
|
|
32
|
+
is not a schema validator for every example. Extending its coverage is tracked
|
|
33
|
+
separately, not a reason to weaken review or bypass required checks.
|
|
34
|
+
|
|
35
|
+
## Done: the monorepo migration is complete
|
|
36
|
+
|
|
37
|
+
**Closed 2026-09-19.** `urlcode-ui`, `urlcode-auth` and `urlcode-admin` are
|
|
38
|
+
workspace packages under `packages/`, and all three have been released from
|
|
39
|
+
this repository — `@jimhoyd/urlcode-ui@0.1.0-alpha.6`,
|
|
40
|
+
`@jimhoyd/urlcode-auth@0.1.0-alpha.6`, `@jimhoyd/urlcode-admin@0.1.0-alpha.4`,
|
|
41
|
+
each on `alpha` with `latest` deliberately held behind. Core's dist-tags are
|
|
42
|
+
unchanged. The three source repositories are gone; their history survives only
|
|
43
|
+
as verified `git bundle`s, because the repository allows squash merges only and
|
|
44
|
+
the imported commits did not survive onto `main`.
|
|
45
|
+
|
|
46
|
+
The operational runbook is [DEVELOPMENT-PIPELINE.md](../../DEVELOPMENT-PIPELINE.md)
|
|
47
|
+
and [RELEASE-SECURITY.md](../../RELEASE-SECURITY.md). The plan itself is archived at
|
|
48
|
+
[archive/2026-09-19/SPIKE-MONOREPO.md](../2026-09-19/SPIKE-MONOREPO.md),
|
|
49
|
+
whose closing note records what the plan got wrong — chiefly that its
|
|
50
|
+
strongest argument, the reach of the enforcing checks, only became true after
|
|
51
|
+
both checkers were changed to discover workspace packages.
|
|
52
|
+
|
|
53
|
+
The section below is kept for the middleware decision it records, which is
|
|
54
|
+
still the reason there is no `packages/middleware`.
|
|
55
|
+
|
|
56
|
+
## Accepted: monorepo first — middleware withdrawn rather than consolidated
|
|
57
|
+
|
|
58
|
+
The maintainer confirmed that monorepo work is starting now. The earlier
|
|
59
|
+
recommendation to keep repositories separate for now is superseded.
|
|
60
|
+
|
|
61
|
+
**Reversed 2026-09-19: the middleware half of this decision no longer applies.**
|
|
62
|
+
This section used to say "move middleware into the monorepo as its own package
|
|
63
|
+
first, preserving its existing API and behavior," and explicitly: "do not
|
|
64
|
+
unpublish or retire the middleware package as part of the initial move."
|
|
65
|
+
That instruction was overtaken. `@jimhoyd/urlcode-middleware` has been
|
|
66
|
+
**unpublished** from npm at `0.1.0-alpha.2` and `jimhoyd-com/urlcode-middleware`
|
|
67
|
+
**deleted** — the package was withdrawn outright instead of migrated, so there
|
|
68
|
+
is no `packages/middleware` to create and no subsequent fold-into-core step.
|
|
69
|
+
The reversal is recorded here rather than deleted because the instruction it
|
|
70
|
+
replaces was explicit, and a reader who remembers it should be able to see that
|
|
71
|
+
it was changed deliberately and not simply forgotten.
|
|
72
|
+
|
|
73
|
+
Nothing was lost in capability terms: per-route middleware is **native to
|
|
74
|
+
core** via the `middleware:` array ([MIDDLEWARE.md](../../MIDDLEWARE.md)), and the
|
|
75
|
+
deleted package only ever offered the same behavior through the extension
|
|
76
|
+
seam. Trust remains the default and explicit sandbox choices keep their
|
|
77
|
+
meaning. The generic extension wrapping hook (`ExtensionInstance.middleware`,
|
|
78
|
+
`RuntimeExtension.cacheSensitive`) stays in core's contract for other
|
|
79
|
+
extensions — it is no longer exercised by any shipped package, which is worth
|
|
80
|
+
knowing before it is assumed to be covered. Static targets continue rejecting
|
|
81
|
+
request-time middleware because there is no server to run it.
|
|
82
|
+
|
|
83
|
+
Migration scope is therefore **core, auth, admin and UI**. Template and the
|
|
84
|
+
distribution tap stay outside that package move. The observed stale peer pins
|
|
85
|
+
and checkout-limited guidance checks strengthen the case for shared
|
|
86
|
+
verification: carry those checks across the new package paths, since merely
|
|
87
|
+
moving files does not prove every generated skill is covered. The cleanup PRs
|
|
88
|
+
that superseded the earlier zero-open-PR survey have since merged, and a fresh
|
|
89
|
+
survey again reports zero open pull requests across all four in-scope
|
|
90
|
+
repositories — re-run it per repository immediately before that repository
|
|
91
|
+
moves rather than trusting this line.
|
|
92
|
+
|
|
93
|
+
The [archived monorepo plan](../2026-09-19/SPIKE-MONOREPO.md) records
|
|
94
|
+
migration context.
|
|
95
|
+
[Issue 172](https://github.com/jimhoyd-com/urlcode/issues/172), which tracked
|
|
96
|
+
"consolidate middleware into core after moving it into the monorepo," was
|
|
97
|
+
**closed on 2026-09-19** as moot — there was nothing left to consolidate.
|
|
98
|
+
Migration starting is not a claim that it has landed.
|
|
99
|
+
|
|
100
|
+
## Source review baseline
|
|
101
|
+
|
|
102
|
+
| Repository | Reviewed commit | Code checked |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| core | `db375bf` | Runtime dispatch, schema normalization, capabilities, static compiler, prerender, MCP and resource generators |
|
|
105
|
+
| auth | `71957dd` | Lifecycle hooks, UI rendering and shared helper imports |
|
|
106
|
+
| admin | `f3b4882` | UI rendering, auth-service integration and shared helper imports |
|
|
107
|
+
| UI | `0e96f7f` | Shared forms, kit/host exports and copied core contract |
|
|
108
|
+
| ~~middleware~~ | `f201f4b` | Extension wrapping, per-entry sandbox dispatch and scaffolding — **repository deleted 2026-09-19; this baseline is unreachable except through the local `urlcode-middleware.bundle`** |
|
|
109
|
+
| template | `4e09e50` | Exact core pin, generated guide and both vendored skills |
|
|
110
|
+
|
|
111
|
+
The Homebrew tap (`73eaaef`) still selects stable core `0.3.0`; its old trust
|
|
112
|
+
behavior belongs to that pin and must not be rewritten as alpha.2 behavior.
|
|
113
|
+
The other organization tap and Scoop bucket contain Gitroll, not URLCode.
|
|
114
|
+
The deleted documentation repository is historical context, not a second source
|
|
115
|
+
of current contracts; its former GitHub links no longer resolve. This review is targeted source inspection, not an audit of
|
|
116
|
+
every execution path or an independent security assessment.
|
package/docs/archive/README.md
CHANGED
|
@@ -20,6 +20,7 @@ Archived on 2026-09-19 after checking the current code:
|
|
|
20
20
|
| [Usability review](2026-09-19/USABILITY-REVIEW.md) | Predates trusted execution and retired packages | New benchmark observations |
|
|
21
21
|
| [Management security](2026-09-19/MANAGEMENT-SECURITY.md) | Describes the removed core link-management API | No current core replacement |
|
|
22
22
|
| [Lambda compile spike](2026-09-19/SPIKE-LAMBDA-COMPILE.md) | Proposal declined 2026-09-19 in favour of one Node deployment per project | Open decisions; AWS and Vercel guides |
|
|
23
|
+
| [Completed decisions (2026-09-20)](2026-09-20/OPEN-DECISIONS-COMPLETED.md) | Closed questions, the completed monorepo migration and the 2026-09-19 source baseline, moved out of open decisions (#242) | Current open decisions and release readiness |
|
|
23
24
|
|
|
24
25
|
Open proposals stay outside the archive with their status stated at the top.
|
|
25
26
|
The accepted trust-by-default decision remains directly linked from the current
|
package/llms-full.txt
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- Generated by scripts/build-llms-full.ts (npm run docs:llms). Do not edit; edit the source documents. -->
|
|
2
|
-
<!-- Consolidated URLCode authoring reference: 16 documents, about 52,
|
|
2
|
+
<!-- Consolidated URLCode authoring reference: 16 documents, about 52,118 tokens (estimate: characters / 4). -->
|
|
3
3
|
|
|
4
4
|
# URLCode
|
|
5
5
|
|
|
@@ -540,9 +540,10 @@ either `true` or `false`) or `sandbox: true` is enough to silence it.
|
|
|
540
540
|
|
|
541
541
|
The same judgment call applies to a project-level lifecycle hook an
|
|
542
542
|
extension invokes (`onSignUp`, `beforeRegister` and the like) — it is
|
|
543
|
-
first-party project code with the same trusted-by-default rule
|
|
544
|
-
`
|
|
545
|
-
|
|
543
|
+
first-party project code with the same trusted-by-default rule as a
|
|
544
|
+
`function`/`middleware` route. Extension hook contract v1 is trusted-only;
|
|
545
|
+
`sandbox: true` is rejected rather than silently ignored. See
|
|
546
|
+
[EXTENSIONS.md](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md#project-level-lifecycle-hooks).
|
|
546
547
|
|
|
547
548
|
Guest TypeScript needs `build-typescript --project SOURCE --out NEW_DIRECTORY`
|
|
548
549
|
before serving. Only the emitted `.js`/`.mjs` executes, in QuickJS for a
|
|
@@ -561,7 +562,7 @@ origin grants pinned to the project revision. These are self-hosted features;
|
|
|
561
562
|
providers refuse them. Signals are bounded best effort with drops, no retries
|
|
562
563
|
or persistence. Never turn a user request into an implicit network grant.
|
|
563
564
|
|
|
564
|
-
Before using a feature, ask `urlcode capabilities <name>` for its constraints, grants and target support and `urlcode schema <path>` for only that YAML fragment (MCP: `get_capability`, `get_schema`), instead of guessing.
|
|
565
|
+
Before using a feature, ask `urlcode capabilities <name>` for its constraints, grants and target support and `urlcode schema <path>` for only that YAML fragment (MCP: `get_capability`, `get_schema`), instead of guessing. For an installed extension, use `urlcode extensions --host-file ... --json` or MCP `get_extensions`; its hook contracts include the accepted names, purpose and input/output schemas. Prefer extension configuration and UI copy/templates/theme/CSS, then a declared project hook, and only then a new extension or fork.
|
|
565
566
|
The [tooling SDK and stdio MCP](https://github.com/jimhoyd-com/urlcode/blob/main/docs/TOOLING.md) inspect, validate, explain and preview
|
|
566
567
|
without guest execution, environment reads or writes. Run `urlcode explain /route`
|
|
567
568
|
to check effective methods, policies and cache outcome, and `urlcode manifest`
|
|
@@ -2702,29 +2703,18 @@ Node adapter conformance is not a live-provider deployment claim.
|
|
|
2702
2703
|
|
|
2703
2704
|
### Project-level lifecycle hooks
|
|
2704
2705
|
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
**The pattern.** An extension with lifecycle events an author judges worth
|
|
2719
|
-
exposing should let the project name its own function in the extension's own
|
|
2720
|
-
`config`, using the same source shape `function`/`middleware` routes already
|
|
2721
|
-
use (a string path, or `{source, export, args}` — `schemas/urlcode.schema.json`),
|
|
2722
|
-
and add its own `sandbox` boolean next to it (below). The extension's own
|
|
2723
|
-
`activate()` reads that config, and its own runtime dispatch — not a new core
|
|
2724
|
-
primitive, an ordinary call the extension package makes with the request
|
|
2725
|
-
context it already has — invokes the named function at the lifecycle point
|
|
2726
|
-
it defines, with a typed input and a typed verdict the extension's own
|
|
2727
|
-
schema documents. For example, an auth-style extension might declare:
|
|
2706
|
+
Extensions expose project customization points through the core hook primitive.
|
|
2707
|
+
Each registration publishes `hooks`, a machine-readable list containing the
|
|
2708
|
+
hook name, whether it is a value-transforming `filter` or side-effect `action`,
|
|
2709
|
+
its description and its input/output JSON Schemas. The extension embeds
|
|
2710
|
+
`extensionHooksSchema(contracts)` in its configuration schema and calls
|
|
2711
|
+
`loadExtensionHooks(config.hooks, contracts, context)` during activation.
|
|
2712
|
+
Core then enforces the common source/export shape, project-root confinement,
|
|
2713
|
+
known names, eager module/export validation, input/output schemas and reload
|
|
2714
|
+
cache busting. Hook entry bytes participate in the project revision, so editing
|
|
2715
|
+
a hook invalidates the operator's extension pin.
|
|
2716
|
+
|
|
2717
|
+
Projects select those declared hooks in the extension's own configuration:
|
|
2728
2718
|
|
|
2729
2719
|
```yaml
|
|
2730
2720
|
extensions:
|
|
@@ -2740,76 +2730,51 @@ extensions:
|
|
|
2740
2730
|
```
|
|
2741
2731
|
|
|
2742
2732
|
with `beforeRegister` called before an account is created, given a typed
|
|
2743
|
-
`{email,
|
|
2733
|
+
`{email, profile?}` input and returning a typed verdict (`{allow: true}`
|
|
2744
2734
|
or `{allow: false, reason}`), and `onSignUp` called after, for side effects
|
|
2745
|
-
such as provisioning a workspace.
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
project code
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
unchanged file URL makes a second activation in the same process keep
|
|
2784
|
-
serving the hook code that was on disk at the first one
|
|
2785
|
-
(jimhoyd-com/urlcode#198). Only the hook's **entry** module is refreshed
|
|
2786
|
-
this way — modules the hook itself imports stay on Node's module cache,
|
|
2787
|
-
the same limitation the trusted route path has, so a change to a hook's
|
|
2788
|
-
own dependency still needs a process restart.
|
|
2789
|
-
- **Sandboxed (`sandbox: true`).** `@jimhoyd/urlcode/sandbox` exports
|
|
2790
|
-
`SandboxPool`, the same QuickJS/worker-thread engine that backs a
|
|
2791
|
-
sandboxed `function`/`middleware` route — the identical module-allowlist
|
|
2792
|
-
walk, memory/stack limits, two-layer deadline enforcement, `maxBytes` and
|
|
2793
|
-
response-shape validation, with no separate or weaker engine for
|
|
2794
|
-
extensions. It takes an explicit list of `{source, export}` entries
|
|
2795
|
-
(resolve a hook's `source` string with the re-exported `functionFile()`,
|
|
2796
|
-
the same resolution/validation a native route's `source` gets) instead of
|
|
2797
|
-
anything route/YAML-shaped, and `execute({entry, chain}, request, context)`
|
|
2798
|
-
in place of a `FunctionRoute`. There is no "trusted" mode exported
|
|
2799
|
-
alongside it — `SandboxPool` is only ever the isolated path; see
|
|
2800
|
-
[FUNCTION-SECURITY.md](https://github.com/jimhoyd-com/urlcode/blob/main/docs/FUNCTION-SECURITY.md) and
|
|
2801
|
-
[TYPESCRIPT.md](https://github.com/jimhoyd-com/urlcode/blob/main/docs/TYPESCRIPT.md) for the full contract.
|
|
2802
|
-
|
|
2803
|
-
An extension honoring a hook's `sandbox: true` is expected to actually
|
|
2804
|
-
isolate that invocation through `SandboxPool` now that the primitive exists
|
|
2805
|
-
(or document plainly that it does not yet, rather than accepting the field
|
|
2806
|
-
and silently running it trusted) — say which, in the extension's own docs,
|
|
2807
|
-
so an author reading them is not misled about what opt-in exists.
|
|
2735
|
+
such as provisioning a workspace. Hook names and lifecycle timing remain the
|
|
2736
|
+
extension's domain, while their declaration, loading and discovery are shared.
|
|
2737
|
+
|
|
2738
|
+
Hooks are first-party project code and run trusted in-process by default, with
|
|
2739
|
+
full Node access, like trusted `function` and `middleware` routes. Contract v1
|
|
2740
|
+
does not define an arbitrary-value sandbox hook protocol. A hook reference with
|
|
2741
|
+
`sandbox: true` is rejected during activation rather than silently run trusted.
|
|
2742
|
+
Only the entry module is refreshed during reactivation; its imported dependencies
|
|
2743
|
+
remain in Node's module cache until restart.
|
|
2744
|
+
|
|
2745
|
+
The UI extension exposes `transformView`, a synchronous filter called before a
|
|
2746
|
+
named kit template renders. It receives `{template, view}` and returns the view
|
|
2747
|
+
model to render. Use copy, templates, theme and CSS for ordinary presentation
|
|
2748
|
+
changes; use this hook for project-specific computed view data that those
|
|
2749
|
+
declarative layers cannot express.
|
|
2750
|
+
|
|
2751
|
+
### Building an extension
|
|
2752
|
+
|
|
2753
|
+
An extension package should export a registration factory and, when it supports
|
|
2754
|
+
`urlcode init --with`, a side-effect-free `scaffold` function. The registration:
|
|
2755
|
+
|
|
2756
|
+
1. Declares its logical name, contract version, supported targets, exact project
|
|
2757
|
+
revision pin and strict configuration/policy schemas.
|
|
2758
|
+
2. Publishes every project hook through `hooks` and reuses
|
|
2759
|
+
`extensionHooksSchema` plus `loadExtensionHooks`; it does not implement its
|
|
2760
|
+
own path resolver or dynamic-import cache.
|
|
2761
|
+
3. Activates all configuration, files, services and hooks before serving a
|
|
2762
|
+
request. Invalid or stale configuration fails activation.
|
|
2763
|
+
4. Returns `handle` for mounts and optionally `authorize`/`middleware` for route
|
|
2764
|
+
policies. It closes resources it owns.
|
|
2765
|
+
5. Keeps credentials, storage and provider setup in the operator host. Project
|
|
2766
|
+
YAML contains logical configuration and project-relative hook references.
|
|
2767
|
+
|
|
2768
|
+
Consumers install the package, declare its YAML block and mounts/policies, and
|
|
2769
|
+
register it in `host.mjs`. They modify it through declared configuration,
|
|
2770
|
+
presentation layers and hooks. A fork is reserved for changing behavior the
|
|
2771
|
+
extension has not exposed; that is evidence for a new declarative field or hook.
|
|
2772
|
+
See [Composing a site](https://github.com/jimhoyd-com/urlcode/blob/main/docs/COMPOSING-A-SITE.md) for the complete ui/auth/admin example.
|
|
2808
2773
|
|
|
2809
2774
|
### Discovering schemas
|
|
2810
2775
|
|
|
2811
2776
|
Each registration carries the JSON Schemas that validate its `config` block and
|
|
2812
|
-
its per-route policy requirements. `urlcode extensions` prints them together with
|
|
2777
|
+
its per-route policy requirements, plus its hook contracts. `urlcode extensions` prints them together with
|
|
2813
2778
|
the project's own declarations so an author can see what a mount accepts:
|
|
2814
2779
|
|
|
2815
2780
|
```sh
|
|
@@ -2818,6 +2783,7 @@ urlcode extensions --project ./site --host-file /absolute/operator/host.mjs [--j
|
|
|
2818
2783
|
|
|
2819
2784
|
For every registration in the host file it reports the name, contract version,
|
|
2820
2785
|
targets, credential headers, configuration schema, policy schema (if any),
|
|
2786
|
+
declared hook names, kinds, descriptions and input/output schemas,
|
|
2821
2787
|
whether the project declares it, whether its `projectSha256` matches the current
|
|
2822
2788
|
revision, the routes that mount it and the routes whose policies require it.
|
|
2823
2789
|
Declared names the host does not register are listed as unregistered. The command
|
|
@@ -3162,15 +3128,24 @@ extensions:
|
|
|
3162
3128
|
onSignUp: ./hooks/on-signup.mjs
|
|
3163
3129
|
```
|
|
3164
3130
|
|
|
3165
|
-
`hooks`
|
|
3166
|
-
|
|
3167
|
-
|
|
3131
|
+
`hooks` remains each package's own config, but core supplies the reference
|
|
3132
|
+
schema, trusted loader and machine-readable hook contract. `get_extensions`
|
|
3133
|
+
reports those contracts, so an agent can discover accepted names, purpose and
|
|
3134
|
+
input/output shapes without guessing from prose.
|
|
3168
3135
|
|
|
3169
3136
|
Hooks are first-party project code and run **trusted and in-process**, the
|
|
3170
3137
|
same default `function` and `middleware` routes have
|
|
3171
|
-
([FUNCTION-SECURITY.md](https://github.com/jimhoyd-com/urlcode/blob/main/docs/FUNCTION-SECURITY.md)).
|
|
3172
|
-
|
|
3173
|
-
|
|
3138
|
+
([FUNCTION-SECURITY.md](https://github.com/jimhoyd-com/urlcode/blob/main/docs/FUNCTION-SECURITY.md)). Extension hook contract v1 is
|
|
3139
|
+
trusted-only; `sandbox: true` is rejected loudly at activation.
|
|
3140
|
+
|
|
3141
|
+
#### `@jimhoyd/urlcode-ui`
|
|
3142
|
+
|
|
3143
|
+
| Hook | Input | Returns | Called |
|
|
3144
|
+
|---|---|---|---|
|
|
3145
|
+
| `transformView` | `{template, view}` | the view object to render | Synchronously before each public `ui.kit.render()` or `ui.kit.page()` call. Use it only when theme, copy, templates and CSS cannot express the change. |
|
|
3146
|
+
|
|
3147
|
+
For example, `transformView: ./hooks/ui-view.mjs` can add project-computed
|
|
3148
|
+
navigation or labels to an auth/admin view without editing either package.
|
|
3174
3149
|
|
|
3175
3150
|
#### `@jimhoyd/urlcode-auth`
|
|
3176
3151
|
|
|
@@ -3338,14 +3313,11 @@ machinery either way.
|
|
|
3338
3313
|
|
|
3339
3314
|
This engine — worker spawning, the module-allowlist walk, the two-layer
|
|
3340
3315
|
deadline, `maxBytes` and response-shape validation — is one implementation
|
|
3341
|
-
shared by route dispatch and by `@jimhoyd/urlcode/sandbox`'s `SandboxPool
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
`sandbox: true`
|
|
3345
|
-
|
|
3346
|
-
through that entry point; there is no separate, weaker sandbox for extensions
|
|
3347
|
-
to reach for, and no "trusted" mode exported there — trusted execution needs
|
|
3348
|
-
no primitive at all (docs/SPIKE-DEFAULT-TRUST-MODEL.md).
|
|
3316
|
+
shared by route dispatch and by `@jimhoyd/urlcode/sandbox`'s `SandboxPool`.
|
|
3317
|
+
That public HTTP-shaped primitive remains available to extension authors, but
|
|
3318
|
+
project extension hooks use arbitrary typed values and contract v1 runs them
|
|
3319
|
+
trusted in-process; it rejects `sandbox: true` rather than claiming HTTP sandbox
|
|
3320
|
+
semantics apply to them. See [extensions](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md#project-level-lifecycle-hooks).
|
|
3349
3321
|
|
|
3350
3322
|
### What the trusted default can and can't do
|
|
3351
3323
|
|
package/llms.txt
CHANGED
|
@@ -66,7 +66,7 @@ fields or bypass target limits or operator grants. See [the design principle](do
|
|
|
66
66
|
- [Interchange](docs/INTERCHANGE.md), [bulk import](docs/BULK.md), [recipes](docs/RECIPES.md) (`urlcode recipes search`, `examples search`), [TypeScript guests](docs/TYPESCRIPT-AUTHORING.md).
|
|
67
67
|
|
|
68
68
|
## Extensions (accounts, administration, presentation)
|
|
69
|
-
- [Extensions](docs/EXTENSIONS.md): `extensions.<name>` blocks,
|
|
69
|
+
- [Extensions](docs/EXTENSIONS.md): `extensions.<name>` blocks, mounts, policies, operator registration, the shared trusted hook primitive and its machine-readable hook contracts. UI/auth/admin project hooks run trusted in-process; contract v1 rejects `sandbox: true`.
|
|
70
70
|
- [Composing a site](docs/COMPOSING-A-SITE.md): what `urlcode init site --with ui,auth,admin` wires, which `--with` combinations are supported, the presentation override paths under `ui/`, and per-package lifecycle hook input, verdict, timing and failure semantics. Separates declarative configuration, project functions and the TypeScript a new extension needs.
|
|
71
71
|
- [urlcode-auth](packages/auth): npm: @jimhoyd/urlcode-auth; accounts, sessions, MFA, roles, account page; its own llms.txt. Lives in this repository as a workspace package.
|
|
72
72
|
- [urlcode-admin](packages/admin): npm: @jimhoyd/urlcode-admin; users, sessions, roles, audit, cases; its own llms.txt. Lives in this repository as a workspace package.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "urlcode",
|
|
3
3
|
"description": "Authoring and operating URLCode projects: the implemented YAML contract, capability limits, deployment and verification commands for the pinned runtime revision.",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "jimhoyd-com",
|
|
7
7
|
"url": "https://github.com/jimhoyd-com"
|
|
@@ -30,6 +30,11 @@ Start with `urlcode context --project <dir> --budget 4000`, then retrieve the
|
|
|
30
30
|
capability, schema fragment, recipe or example relevant to the change. Use the
|
|
31
31
|
read-only MCP equivalents when available. `llms.txt` is the index; read the
|
|
32
32
|
matching task guide from `docs/` when a query needs more explanation.
|
|
33
|
+
When the project has an operator host file, inspect `urlcode extensions
|
|
34
|
+
--project <dir> --host-file <absolute-file> --json` (MCP: `get_extensions`)
|
|
35
|
+
before writing extension configuration or project hooks. The report is the
|
|
36
|
+
machine-readable source for config/policy schemas and hook names, purposes and
|
|
37
|
+
input/output schemas.
|
|
33
38
|
`docs/SPECIFICATION.md` and `schemas/urlcode.schema.json` resolve contract
|
|
34
39
|
questions. Archived plans are historical, not valid YAML guidance.
|
|
35
40
|
|
|
@@ -49,6 +54,11 @@ questions. Archived plans are historical, not valid YAML guidance.
|
|
|
49
54
|
- Create every referenced module, page and asset **before** validating. All
|
|
50
55
|
source paths resolve from the project root. Trusted modules can import Node built-ins and npm packages;
|
|
51
56
|
only `sandbox: true` modules are restricted to the relative snapshotted graph.
|
|
57
|
+
- Customize installed extensions in this order: declarative configuration;
|
|
58
|
+
`urlcode-ui` copy, theme, template and CSS overrides; a hook declared by the
|
|
59
|
+
extension; a new extension only when the installed contract cannot express
|
|
60
|
+
the behavior. Extension hooks run trusted in-process and reject `sandbox:
|
|
61
|
+
true` in contract v1.
|
|
52
62
|
- Write exact response fixtures for success and failure, covering every active
|
|
53
63
|
method, middleware behavior, HEAD, and any range or cache semantics.
|
|
54
64
|
- Follow `docs/BEST-PRACTICES.md` for layout and readability as the project grows.
|