@jimhoyd/urlcode 0.4.0-alpha.2 → 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/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 +59 -64
- package/ROADMAP.md +66 -376
- package/dist/BUILD-MANIFEST.json +26 -25
- package/dist/agents-guide.js +6 -6
- package/dist/authoring.js +15 -1
- package/dist/build-static.js +2 -0
- package/dist/capability-query.js +0 -1
- package/dist/catalog.js +0 -1
- package/dist/cli.js +25 -9
- package/dist/config.js +1 -1
- package/dist/explain-cli.js +4 -2
- package/dist/explain.js +8 -2
- package/dist/extensions.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 +8 -2
- package/dist/mcp-authoring.js +4 -4
- package/dist/mcp.js +1 -1
- package/dist/policies/cache.js +2 -2
- package/dist/policies.js +3 -1
- package/dist/prerender.js +4 -0
- package/dist/project-dependencies.js +305 -0
- package/dist/readiness.js +3 -0
- package/dist/route-diff.js +12 -5
- 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 +6 -1
- package/dist/types/extensions.d.ts +1 -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 +5 -2
- package/dist/types/project-dependencies.d.ts +78 -0
- package/dist/types/readiness.d.ts +2 -0
- package/dist/types/trusted-functions.d.ts +1 -4
- 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 +15 -7
- package/docs/ASSETS.md +2 -1
- package/docs/AUTH-BACKUP.md +32 -0
- package/docs/AWS.md +9 -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 +284 -0
- package/docs/COMPOSING-A-SITE.md +278 -0
- package/docs/DEVELOPMENT-PIPELINE.md +270 -0
- package/docs/EXTENSIONS.md +51 -11
- package/docs/FRAMEWORK.md +67 -48
- package/docs/FUNCTION-SECURITY.md +44 -0
- package/docs/INSTALL.md +13 -8
- package/docs/LOCAL-DEVELOPMENT.md +3 -0
- package/docs/MIDDLEWARE.md +10 -4
- package/docs/OPEN-DECISIONS.md +224 -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 +10 -5
- package/docs/README.md +20 -44
- package/docs/RELEASE-0.4.0-alpha.3.md +50 -0
- package/docs/RELEASE-0.4.1.md +73 -0
- package/docs/RELEASE-READINESS.md +6 -6
- package/docs/RELEASE-SECURITY.md +96 -181
- package/docs/RESILIENCE.md +3 -3
- package/docs/ROUTING.md +3 -4
- package/docs/SECURITY-AUDIT.md +2 -2
- package/docs/SPECIFICATION.md +12 -6
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +6 -5
- package/docs/SPIKE-BUSINESS-SUITE.md +14 -6
- package/docs/SPIKE-CORE-LAYERING.md +49 -18
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +7 -5
- package/docs/STARTERS.md +17 -5
- package/docs/STATIC.md +14 -3
- package/docs/TOOLING.md +10 -7
- package/docs/TYPESCRIPT-AUTHORING.md +6 -1
- package/docs/VERCEL.md +10 -2
- package/docs/VERSION-ALIGNMENT.md +76 -201
- 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 +387 -0
- package/docs/{SPIKE-EXTENSION-MODEL.md → archive/2026-09-19/SPIKE-EXTENSION-MODEL.md} +11 -0
- package/docs/{SPIKE-EXTENSIONS.md → archive/2026-09-19/SPIKE-EXTENSIONS.md} +25 -14
- package/docs/archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md +365 -0
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +778 -0
- package/docs/{USABILITY-REVIEW.md → archive/2026-09-19/USABILITY-REVIEW.md} +12 -2
- package/docs/archive/README.md +28 -0
- package/docs/policies/agents.md +1 -1
- package/docs/policies/compression.md +3 -2
- package/docs/policies/security.md +3 -2
- package/docs/yaml/functions.md +10 -2
- package/docs/yaml/middleware.md +5 -3
- package/examples/assets/example.yaml +1 -1
- package/examples/cookbook/middleware/envelope.mjs +4 -2
- 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 +503 -88
- package/llms.txt +6 -4
- package/package.json +27 -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/middleware/middleware/envelope.mjs +4 -2
- 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-LAMBDA-COMPILE.md +0 -201
- package/docs/SPIKE-MONOREPO.md +0 -322
package/docs/RELEASE-SECURITY.md
CHANGED
|
@@ -1,181 +1,96 @@
|
|
|
1
|
-
# Candidate and release security
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
## What broke on every first release, and why
|
|
99
|
-
|
|
100
|
-
Four repositories (core, ui, auth, admin) each cut their first tagged release in
|
|
101
|
-
the same session. Every one hit a subset of the same bugs, because each
|
|
102
|
-
`release.yml` was written and reviewed but never actually run end-to-end against
|
|
103
|
-
a real tag before. None of this is repository-specific; check for all of it
|
|
104
|
-
before trusting an unexercised release workflow:
|
|
105
|
-
|
|
106
|
-
- **`npm ci` on the bare runner's root-owned npm.** A build that runs entirely
|
|
107
|
-
inside a pinned Docker image never calls `actions/setup-node`, so a later
|
|
108
|
-
`npm install --global npm@11.5.1` (needed for the trusted-publishing floor)
|
|
109
|
-
hits the runner's preinstalled, root-owned npm and fails `EACCES`. Add
|
|
110
|
-
`actions/setup-node` before any step that installs global npm packages, even
|
|
111
|
-
if the main build never touches the runner's own Node.
|
|
112
|
-
- **The floor check must run after the pin, not before.** A guard asserting
|
|
113
|
-
"npm ≥ 11.5.1" is useless directly after `setup-node` with `node-version: '22'`,
|
|
114
|
-
which bundles npm ~10.9.x — it can never pass. The floor only means something
|
|
115
|
-
once the publish step's own `npm install --global npm@11.5.1` has actually run.
|
|
116
|
-
- **`npm publish` refuses an unqualified prerelease.** `You must specify a tag
|
|
117
|
-
using --tag when publishing a prerelease version.` npm's safety default is
|
|
118
|
-
`latest`; a prerelease must derive an explicit dist-tag from its version
|
|
119
|
-
(`0.1.0-alpha.1` → `alpha`, anything without a `-` → `latest`) and pass
|
|
120
|
-
`--tag`. This path is only exercised by a package's *first* prerelease, so it
|
|
121
|
-
silently sat broken in every repository until each hit it for the first time.
|
|
122
|
-
- **`npm pack --pack-destination candidate` needs `candidate/` to exist first.**
|
|
123
|
-
npm does not create the destination directory; `mkdir -p candidate` first.
|
|
124
|
-
- **A private repository's unauthenticated `git fetch origin main` cannot work.**
|
|
125
|
-
If checkout uses `persist-credentials: false` (correct, for a step that
|
|
126
|
-
should not need write access) and the repo is private, `git fetch` fails
|
|
127
|
-
`could not read Username for 'https://github.com'` before ever reaching the
|
|
128
|
-
version check. Compare the tag against main through the GitHub API instead
|
|
129
|
-
(`gh api repos/OWNER/REPO/compare/main...SHA --jq .status`, expecting
|
|
130
|
-
`identical` or `behind`) — it needs no credentials and stays read-only. Public
|
|
131
|
-
repositories can keep the plain fetch; it works there.
|
|
132
|
-
- **`--conditions=development` in `npm run verify`'s test script resolves peers
|
|
133
|
-
to source that a real npm install never ships.** The regular CI job symlinks
|
|
134
|
-
sibling checkouts in place of `node_modules`, so `./src/*.ts` exists and the
|
|
135
|
-
flag is correct there. A release installs real published tarballs of its
|
|
136
|
-
peers, which only ever ship `dist/`, so the same flag makes every import of a
|
|
137
|
-
peer fail `ERR_MODULE_NOT_FOUND`. Drop the flag for the release-workflow test
|
|
138
|
-
invocation specifically (run `node scripts/check-sqlite.mjs` explicitly first,
|
|
139
|
-
since bypassing `npm test` skips that pretest hook), and audit any test file
|
|
140
|
-
that separately hardcodes the flag in a spawned child process — it has to be
|
|
141
|
-
fixed the same way, independently, wherever it appears.
|
|
142
|
-
- **A peer-install command with the wrong flag combination is a silent no-op.**
|
|
143
|
-
`npm install --no-save --no-package-lock --ignore-scripts --legacy-peer-deps
|
|
144
|
-
<peer>@<version>` installed *nothing*, with no error, when the target package
|
|
145
|
-
names already appear in `peerDependencies` — `npm ci` earlier reports "added N
|
|
146
|
-
packages" as if it worked. Confirm the install actually happened
|
|
147
|
-
(`ls node_modules/@scope/*/package.json` and print each version) rather than
|
|
148
|
-
trusting the exit code; `--no-save --ignore-scripts <specs>` (no
|
|
149
|
-
`--no-package-lock`, no `--legacy-peer-deps`) is the version that works, paired
|
|
150
|
-
with `git diff --exit-code -- package.json package-lock.json` to prove nothing
|
|
151
|
-
was recorded as a dependency.
|
|
152
|
-
- **A version published from an unbuilt checkout is burned forever.** npm never
|
|
153
|
-
allows a version to be replaced. `@jimhoyd/urlcode-auth@0.1.0-alpha.1` reached
|
|
154
|
-
the registry from something other than the CI workflow (a manual `npm
|
|
155
|
-
publish` run before `npm run build` had produced `dist/`), so the published
|
|
156
|
-
tarball contained only metadata files and no code. Every consumer's typecheck
|
|
157
|
-
failed with `Cannot find module '@jimhoyd/urlcode-auth'` — a real, correct
|
|
158
|
-
failure, not a bug in the consumer. The only fix is bumping to a new version
|
|
159
|
-
and publishing that instead; nothing can repair or unpublish the bad one.
|
|
160
|
-
**Never run `npm publish` by hand outside the release workflow** — the
|
|
161
|
-
workflow is the only place that reliably builds before packing.
|
|
162
|
-
- **`ENEEDAUTH` on `npm publish` under trusted publishing usually means the
|
|
163
|
-
registry-side configuration doesn't exist or doesn't permit direct publish
|
|
164
|
-
yet**, not a workflow bug. Trusted publishing needs an entry under the
|
|
165
|
-
package's npm settings ("Trusted Publisher") naming the exact GitHub
|
|
166
|
-
org/repo and workflow filename, with no environment set unless the workflow
|
|
167
|
-
declares one; recent npm UI changes default new configurations to
|
|
168
|
-
"stage publish" only; "allow npm publish" (direct publish, which is what
|
|
169
|
-
this project's workflows do) must be explicitly enabled too. A wrong
|
|
170
|
-
org/repo/workflow match tends to surface as a 404, not `ENEEDAUTH`;
|
|
171
|
-
`ENEEDAUTH` is the signature of no matching configuration existing at all.
|
|
172
|
-
- **Publish order matters and is easy to get backwards.** Extension packages
|
|
173
|
-
declare `@jimhoyd/urlcode >=X <Y` as a peer range; publish core before ui,
|
|
174
|
-
auth or admin, or their own release-workflow peer-install step has nothing
|
|
175
|
-
real to resolve against.
|
|
176
|
-
|
|
177
|
-
None of the above is exotic; all nine bugs were found by actually running each
|
|
178
|
-
workflow against a real tag, one release at a time, and reading the actual
|
|
179
|
-
failure rather than guessing from the workflow source. Treat "the workflow file
|
|
180
|
-
looks right" and "the workflow has actually published successfully once" as two
|
|
181
|
-
different, unrelated claims.
|
|
1
|
+
# Candidate and release security
|
|
2
|
+
|
|
3
|
+
The [development pipeline](DEVELOPMENT-PIPELINE.md) is the operational runbook
|
|
4
|
+
for CI selection, release planning, tags, publication and recovery. The
|
|
5
|
+
[September 19 audit](CI-RELEASE-AUDIT-2026-09-19.md) explains the changes;
|
|
6
|
+
[the prior process and failure history](archive/2026-09-19/RELEASE-SECURITY.md)
|
|
7
|
+
is retained as a historical record, not current instructions.
|
|
8
|
+
|
|
9
|
+
The license remains Apache-2.0. A successful release is not an independent
|
|
10
|
+
security assessment, hostile multi-tenant readiness claim or deployment proof.
|
|
11
|
+
See [release readiness](RELEASE-READINESS.md) and [security](../SECURITY.md).
|
|
12
|
+
|
|
13
|
+
## Source and validation
|
|
14
|
+
|
|
15
|
+
A release must name the checked-out commit, already on main, with successful
|
|
16
|
+
full verification of that exact SHA and CodeQL analysis. Full verification
|
|
17
|
+
means a successful nightly or manual `ci.yml` run across every supported OS/Node
|
|
18
|
+
combination; the compact push/PR matrix alone does not authorize publication. Package manifests,
|
|
19
|
+
lockfile versions and tag names must agree. Published peer floors must exist and
|
|
20
|
+
extension tests must resolve the published packages, not workspace source.
|
|
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,
|
|
25
|
+
verification, runtime audit, package installation tests and local drills precede
|
|
26
|
+
packing. Build commands in that container receive no GitHub token.
|
|
27
|
+
|
|
28
|
+
The candidate workflow stores artifacts without publishing. A tagged release
|
|
29
|
+
publishes npm only when `PUBLISH_NPM=true` and core images only when
|
|
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
|
|
33
|
+
rehearse deployment rollback; Actions retention is not an archival guarantee.
|
|
34
|
+
|
|
35
|
+
## Identity and provenance
|
|
36
|
+
|
|
37
|
+
The four per-package workflow filenames are stable npm trusted-publisher
|
|
38
|
+
identities. npm publishing uses OIDC, npm 11.5.1 and a supported Node version,
|
|
39
|
+
with no long-lived npm token. Renaming a workflow or changing the repository
|
|
40
|
+
requires a reviewed registry trust migration. Successful preparation does not
|
|
41
|
+
prove the registry-side identity permits direct publication.
|
|
42
|
+
|
|
43
|
+
`actions/attest` signs the candidate files with GitHub OIDC/Sigstore provenance.
|
|
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
|
|
50
|
+
TypeScript versions and emitted-file hashes. `dist/` is built, never committed.
|
|
51
|
+
|
|
52
|
+
Verify an artifact with `gh attestation verify <tarball> --repo
|
|
53
|
+
jimhoyd-com/urlcode`, and constrain verification to the expected workflow,
|
|
54
|
+
source ref and source digest for the selected release. Compare the source and
|
|
55
|
+
artifact hashes to the intended release, rather than accepting any signed file.
|
|
56
|
+
A signature establishes provenance, not safety or reproducibility. The npm SBOM
|
|
57
|
+
is not a complete OS/container SBOM; hosted runners remain platform-controlled.
|
|
58
|
+
|
|
59
|
+
## Immutability and channels
|
|
60
|
+
|
|
61
|
+
Never delete/recreate or move a version tag to repair a workflow. A source fix
|
|
62
|
+
requires a new version. Repeating publication of an existing npm version requires
|
|
63
|
+
identical SHA-512 integrity. GitHub assets are compared and missing assets added;
|
|
64
|
+
existing unequal assets are never clobbered. Transient registry errors fail
|
|
65
|
+
closed rather than count as an unpublished version.
|
|
66
|
+
|
|
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
|
|
72
|
+
and container channels cannot regress to an older version. Existing core image
|
|
73
|
+
versions are reused only with matching source labels; unlabeled historical
|
|
74
|
+
images require a reviewed migration rather than an inferred identity.
|
|
75
|
+
|
|
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.
|
|
79
|
+
The active immutable-tag rule blocks release tag updates/deletions with no
|
|
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. A repository-scoped GitHub App is the preferred eventual automation
|
|
82
|
+
identity; a narrowly scoped fine-grained PAT can support a maintainer script.
|
|
83
|
+
|
|
84
|
+
## Remaining validation
|
|
85
|
+
|
|
86
|
+
Keep full supported-OS/Node coverage and archive-install tests green. The actual
|
|
87
|
+
publish path, OIDC trust for every package, tag protection, GHCR behavior and
|
|
88
|
+
partial-failure recovery must be verified on an authorized release; local tests
|
|
89
|
+
and workflow inspection cannot prove them. The release helpers do not alter
|
|
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/RESILIENCE.md
CHANGED
|
@@ -156,6 +156,6 @@ admission/fairness, production metrics/exporters, dedicated slow-reader protecti
|
|
|
156
156
|
provider-level mitigation validation and sustained failure/soak testing. These are
|
|
157
157
|
free-runtime/operator requirements.
|
|
158
158
|
|
|
159
|
-
Core has no durable store of its own to recover; a
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
Core has no durable store of its own to recover; a durable-state extension is
|
|
160
|
+
responsible for its own management listener, backup and recovery procedure. No
|
|
161
|
+
such extension ships today — `urlcode-dynamic-link` has been retired.
|
package/docs/ROUTING.md
CHANGED
|
@@ -84,10 +84,9 @@ any per-visitor session record. Git owns route behavior and code; user-created r
|
|
|
84
84
|
their own persistence, backup and export lifecycle.
|
|
85
85
|
|
|
86
86
|
Core has no native handler for this today: the `link` handler that implemented
|
|
87
|
-
it was removed.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
that package exists.
|
|
87
|
+
it was removed. The `urlcode-dynamic-link` extension package that replaced it
|
|
88
|
+
has been retired and unpublished, so a project needing stored short links owns
|
|
89
|
+
that storage itself.
|
|
91
90
|
|
|
92
91
|
Functions still cannot access databases, the filesystem or network directly.
|
|
93
92
|
General application state and realtime sessions remain future work.
|
package/docs/SECURITY-AUDIT.md
CHANGED
|
@@ -85,7 +85,7 @@ Management event status 0 means no response headers were sent before disconnect.
|
|
|
85
85
|
An aborted request may have committed a mutation: reconcile record/version before
|
|
86
86
|
retrying. These logs are best effort, may be dropped under pressure, identify a configured credential ID (or a legacy shared token), and are not a
|
|
87
87
|
tamper-evident journal. Successful store mutations now have separate transactional
|
|
88
|
-
audit records; see [management security](MANAGEMENT-SECURITY.md). A failed sink needs collector/operator recovery;
|
|
88
|
+
audit records; see [management security](archive/2026-09-19/MANAGEMENT-SECURITY.md). A failed sink needs collector/operator recovery;
|
|
89
89
|
URLCode cannot report failures reliably through the same broken output stream.
|
|
90
90
|
|
|
91
91
|
## Boundaries checked
|
|
@@ -122,7 +122,7 @@ limits. No anonymous code/config upload service is approved by these changes.
|
|
|
122
122
|
collection/action scopes, expiry and hot revocation, plus atomic SQLite mutation
|
|
123
123
|
audits are implemented. Legacy shared tokens remain for compatibility. There is
|
|
124
124
|
no public user-account system, MFA/SSO, built-in rate limiter, credential issuance
|
|
125
|
-
service or external tamper-evident archive. See [management security](MANAGEMENT-SECURITY.md).
|
|
125
|
+
service or external tamper-evident archive. See [management security](archive/2026-09-19/MANAGEMENT-SECURITY.md).
|
|
126
126
|
|
|
127
127
|
**Before claiming operational readiness:** execute sustained mixed-workload soak,
|
|
128
128
|
backup restoration, disk-full, process-kill, proxy timeout and rollback drills on
|
package/docs/SPECIFICATION.md
CHANGED
|
@@ -158,10 +158,10 @@ keys preserve repeated values. Headers are forwarded only through explicit maps.
|
|
|
158
158
|
## Stored links
|
|
159
159
|
|
|
160
160
|
Core has no native `link` handler or `dynamicLinks` project flag; both were
|
|
161
|
-
removed.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
removed. The `urlcode-dynamic-link` extension package that replaced them has
|
|
162
|
+
been retired and unpublished, so a project needing stored short links owns that
|
|
163
|
+
storage itself; there is no in-core replacement or deprecation shim for
|
|
164
|
+
`link`/`dynamicLinks`.
|
|
165
165
|
|
|
166
166
|
## Functions
|
|
167
167
|
|
|
@@ -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,
|
|
@@ -329,7 +333,9 @@ are local Git-owned examples; they grant no capabilities. [Build-time TypeScript
|
|
|
329
333
|
is separate from runtime execution.
|
|
330
334
|
|
|
331
335
|
The [tooling SDK and optional local MCP](TOOLING.md) inspect and validate without
|
|
332
|
-
executing handlers or reading binding values.
|
|
336
|
+
executing handlers or reading binding values. MCP defaults to inspection; the
|
|
337
|
+
explicit `--allow-authoring` option enables bounded project writes, separately
|
|
338
|
+
from host-file loading and grants. [Provider conformance](PROVIDER-VERIFICATION.md)
|
|
333
339
|
distinguishes local adapter replay from actual deployment observations; no real
|
|
334
340
|
provider deployment is implied by CI. [Proxy and signal egress](EGRESS.md) requires
|
|
335
341
|
external revision-pinned operator grants and bounded host-owned transport;
|
|
@@ -32,7 +32,7 @@ Before building the application or a new harness, investigate:
|
|
|
32
32
|
- Agent productivity studies, framework comparison applications and standard
|
|
33
33
|
CRUD/full-stack benchmark applications.
|
|
34
34
|
- URLCode's existing [agent harness](../benchmarks/agent/README.md), authoring
|
|
35
|
-
evals, [next-steps plan](NEXT-STEPS.md), and runtime benchmarks in
|
|
35
|
+
evals, [next-steps plan](archive/2026-09-19/NEXT-STEPS.md), and runtime benchmarks in
|
|
36
36
|
`benchmarks/routing.ts`, `benchmarks/bulk.ts` and
|
|
37
37
|
`benchmarks/sandbox-vs-trusted.ts`.
|
|
38
38
|
|
|
@@ -198,8 +198,9 @@ For each verified, actionable issue include:
|
|
|
198
198
|
- Acceptance criteria and related/duplicate issue links.
|
|
199
199
|
|
|
200
200
|
Use the owning repository's issue template and the ownership map in
|
|
201
|
-
[AGENTS.md](../AGENTS.md): core/runtime issues belong here,
|
|
202
|
-
|
|
201
|
+
[AGENTS.md](../AGENTS.md): core/runtime issues belong here, documentation
|
|
202
|
+
included -- `urlcode-docs` is deleted and cannot accept issues -- and extension
|
|
203
|
+
defects in the corresponding extension repository.
|
|
203
204
|
Update existing issues with new evidence rather than duplicating them. Track
|
|
204
205
|
prepared, filed, duplicate and unverified dispositions in the findings report;
|
|
205
206
|
file verified issues and retain unresolved hypotheses as explicitly unverified
|
|
@@ -220,8 +221,8 @@ current contract when documents and historical benchmark assumptions differ.
|
|
|
220
221
|
|
|
221
222
|
Preserve Agent A's baseline before editing guidance. Then propose the smallest
|
|
222
223
|
authoritative set of changes that makes the principle clear to people and agents,
|
|
223
|
-
including when custom JavaScript is appropriate. Public authoring guidance
|
|
224
|
-
|
|
224
|
+
including when custom JavaScript is appropriate. Public authoring guidance and
|
|
225
|
+
contributor records both belong here, now that `urlcode-docs` is deleted. Do not copy the rule
|
|
225
226
|
everywhere or expose the experimental prompt as product guidance by accident.
|
|
226
227
|
|
|
227
228
|
## Phase 6 — Fastify comparison
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
# URLCode business suite spike
|
|
2
2
|
|
|
3
|
+
> Review update, 2026-09-19: Current status: an unapproved candidate list, gated on observed repetition
|
|
4
|
+
> and benchmark evidence under PROJECT-DIRECTION.md. The short-link products
|
|
5
|
+
> are retired; the seven-product recommendation below is historical, not agreed
|
|
6
|
+
> current scope. No business-suite implementation is implied.
|
|
7
|
+
|
|
8
|
+
|
|
3
9
|
Date: 2026-09-18. Status: proposal, not an implemented contract or production claim.
|
|
4
10
|
Core inspected at `50790d3` (0.4.0-alpha.1), plus local auth, admin, UI and
|
|
5
11
|
shortener source/status files. Competitor research below is a documentation
|
|
6
12
|
review, not hands-on benchmarking. Features and commercial packaging can change.
|
|
7
13
|
|
|
8
14
|
> **Update:** this proposal was written when `link`/`LinkStore` was still a
|
|
9
|
-
> native core feature. Core no longer has that API — it was extracted to
|
|
10
|
-
> separate
|
|
11
|
-
>
|
|
12
|
-
>
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
+
> native core feature. Core no longer has that API — it was extracted to a
|
|
16
|
+
> separate `urlcode-dynamic-link` package, which has since been retired,
|
|
17
|
+
> unpublished and deleted (September 2026), along with the `urlcode-short`
|
|
18
|
+
> shortener this document proposes. Those two products are no longer planned.
|
|
19
|
+
> References below to core owning link storage (e.g. "Reuse core's LinkStore",
|
|
20
|
+
> "Retain core link semantics", the `core LinkStore -> short` dependency line)
|
|
21
|
+
> describe the pre-extraction state this spike was proposing against, and the
|
|
22
|
+
> `urlcode-short` migration sections record an abandoned plan.
|
|
15
23
|
|
|
16
24
|
## Recommendation
|
|
17
25
|
|
|
@@ -1,24 +1,41 @@
|
|
|
1
1
|
# Spike: layering `link` and `middleware` out of core
|
|
2
2
|
|
|
3
|
+
> Review update, 2026-09-19: Current baseline: **both extractions this spike
|
|
4
|
+
> proposes are over, and neither ended in a shipped separate package.** Link
|
|
5
|
+
> extraction completed and the resulting package was then retired. Middleware
|
|
6
|
+
> extraction never completed: core still implements native middleware, and the
|
|
7
|
+
> separate middleware extension — which added a different API with per-entry
|
|
8
|
+
> sandboxing — has itself been unpublished at `0.1.0-alpha.2` and its
|
|
9
|
+
> repository deleted. The extraction direction below is superseded, and it is
|
|
10
|
+
> not an instruction to remove core middleware: core's native `middleware:`
|
|
11
|
+
> array is now the only implementation of per-route middleware there is.
|
|
12
|
+
>
|
|
13
|
+
> An earlier version of this note said "monorepo work is starting now:
|
|
14
|
+
> middleware moves in as a separate package first, and folds into core
|
|
15
|
+
> afterward." Monorepo work is still starting, for core, auth, admin and UI —
|
|
16
|
+
> but the middleware clause is void. There is no package to move in and
|
|
17
|
+
> nothing to fold in afterward.
|
|
18
|
+
|
|
19
|
+
|
|
3
20
|
Status: proposal, nothing implemented. No code in this repository does any of
|
|
4
21
|
this, and nothing here is committed scope. This combines two extraction ideas
|
|
5
22
|
into one spike because they share a mechanism and a sequence, not because
|
|
6
23
|
either is blocked on the other.
|
|
7
24
|
|
|
8
|
-
> **Update:** this spike's `link` extraction (Phase 1 below)
|
|
9
|
-
>
|
|
10
|
-
>
|
|
11
|
-
>
|
|
12
|
-
>
|
|
13
|
-
> this document describe the pre-extraction state this
|
|
14
|
-
> not current core.
|
|
25
|
+
> **Update:** this spike's `link` extraction (Phase 1 below) was implemented —
|
|
26
|
+
> core no longer has a native `link`/`LinkStore`/`dynamicLinks` API — and the
|
|
27
|
+
> separate `urlcode-dynamic-link` package that received it has since been
|
|
28
|
+
> retired, unpublished from npm and its repository deleted (September 2026).
|
|
29
|
+
> Nothing owns stored links today. Present-tense references to `link` as a core
|
|
30
|
+
> feature elsewhere in this document describe the pre-extraction state this
|
|
31
|
+
> spike was analyzing, not current core.
|
|
15
32
|
|
|
16
33
|
## The shape this is aiming at
|
|
17
34
|
|
|
18
35
|
Core's job is to stay the smallest thing that is still a complete product on
|
|
19
36
|
its own: YAML routing (`redirect`/`respond`/`page`/`static`/`download`) plus
|
|
20
37
|
`function`, the one primitive with an actual serverless story
|
|
21
|
-
([`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`
|
|
22
39
|
route to its own Lambda, the pattern `src/build-cloudflare.ts` already uses).
|
|
23
40
|
That base alone serves static and dynamic content and deploys anywhere
|
|
24
41
|
(`docs/FRAMEWORK.md` layer 1–3). Everything past that — accounts, admin,
|
|
@@ -41,7 +58,8 @@ honest instead of inventing two different shapes:
|
|
|
41
58
|
- **Mount ownership** (`route.extension`, `RuntimeExtension.activate` →
|
|
42
59
|
`ExtensionInstance.handle`): an extension owns an exclusive `/prefix/*` and
|
|
43
60
|
answers every request under it. This is the shape `link` needs — it already
|
|
44
|
-
behaves like a self-contained mount (
|
|
61
|
+
behaves like a self-contained mount (documented in the since-retired
|
|
62
|
+
`urlcode-dynamic-link` package), and
|
|
45
63
|
`auth`/`admin` already use it for `/account/*` and `/admin/*`
|
|
46
64
|
(`docs/FRAMEWORK.md:10-15`).
|
|
47
65
|
- **Wrap without owning** (`ExtensionInstance.authorize`, called when a route
|
|
@@ -161,7 +179,15 @@ before anything here is treated as settled.
|
|
|
161
179
|
need to exist or be attached before their Phase 2 work can be written or
|
|
162
180
|
verified, matching the constraint already flagged for `link`.
|
|
163
181
|
|
|
164
|
-
## Repo governance for the two new repos (decided)
|
|
182
|
+
## Repo governance for the two new repos (decided — both repos since deleted)
|
|
183
|
+
|
|
184
|
+
> **Historical, 2026-09-19.** Both repositories this section governs were
|
|
185
|
+
> created, released once, and then deleted; both packages are unpublished. The
|
|
186
|
+
> decisions below were applied while they existed and are kept as the recorded
|
|
187
|
+
> template for any future extension repository — not as a description of
|
|
188
|
+
> anything live. Note in particular that the "published public from the start"
|
|
189
|
+
> departure recorded below is the one whose consequences are worth reading
|
|
190
|
+
> back: both repos that took it are gone within days of their first release.
|
|
165
191
|
|
|
166
192
|
Both `urlcode-dynamic-link` and `urlcode-middleware` follow `GOVERNANCE.md`
|
|
167
193
|
and `AGENTS.md` as written, with one explicit decision recorded here per
|
|
@@ -176,7 +202,8 @@ AGENTS.md's "do not publish packages without an explicit decision":
|
|
|
176
202
|
admins or automation, CODEOWNERS recording ownership. CI/release workflow
|
|
177
203
|
shape copied from core's `release.yml` (candidate build → audit → pack →
|
|
178
204
|
attest → publish via trusted publisher, no long-lived npm token), per the
|
|
179
|
-
pattern `docs/NEXT-STEPS.md`
|
|
205
|
+
pattern §2.1 of the archived plan (`docs/archive/2026-09-19/NEXT-STEPS.md`)
|
|
206
|
+
already used for `auth`/`admin`/`ui`.
|
|
180
207
|
CodeQL required on main, secret scanning and push protection on, same as
|
|
181
208
|
core.
|
|
182
209
|
- **Published public from the start** — both the GitHub repo and the npm
|
|
@@ -225,9 +252,10 @@ system tax." The real costs are narrower and different for each:
|
|
|
225
252
|
cost described above still applies, in full, to whichever specific
|
|
226
253
|
`middleware:` wrap a developer explicitly opts into `sandbox: true` — and
|
|
227
254
|
the same mitigation still matters there: native declarative primitives
|
|
228
|
-
(
|
|
229
|
-
|
|
230
|
-
opt-in at all, for the cases where a
|
|
255
|
+
(the Phase 4.2 `auth: { required: true, role: ... }` direction in the
|
|
256
|
+
archived plan, `docs/archive/2026-09-19/NEXT-STEPS.md`) reduce how much logic
|
|
257
|
+
ever needs to reach for the sandboxed opt-in at all, for the cases where a
|
|
258
|
+
developer does judge it warranted.
|
|
231
259
|
|
|
232
260
|
## Other core pieces considered and set aside
|
|
233
261
|
|
|
@@ -253,14 +281,17 @@ part of the smallest complete product:
|
|
|
253
281
|
them would shrink core below the "complete product on its own" bar rather
|
|
254
282
|
than trim it.
|
|
255
283
|
- **Management API / operator grants / credential policy** — foundation that
|
|
256
|
-
extensions themselves depend on (`docs/MANAGEMENT-SECURITY.md`,
|
|
284
|
+
extensions themselves depend on (`docs/archive/2026-09-19/MANAGEMENT-SECURITY.md`,
|
|
257
285
|
`docs/FUNCTION-SECURITY.md`); moving it out would mean extracting the thing
|
|
258
286
|
the extraction pattern relies on.
|
|
259
287
|
|
|
260
288
|
Nothing else in core matches the `link`/`middleware` shape today. If a third
|
|
261
|
-
candidate is going to be found,
|
|
262
|
-
|
|
263
|
-
defensible way to find it than
|
|
289
|
+
candidate is going to be found, the repetition-log discipline of Phase 6 in the
|
|
290
|
+
archived plan (`docs/archive/2026-09-19/NEXT-STEPS.md`) — extract from observed
|
|
291
|
+
repetition, not speculation — is the more defensible way to find it than
|
|
292
|
+
continuing to eyeball the handler list. That plan records Phase 6 as never
|
|
293
|
+
started, and no `docs/REPETITION-LOG.md` was ever written, so the log itself
|
|
294
|
+
would have to be started before it could inform the decision.
|
|
264
295
|
|
|
265
296
|
Recommendation: before either Phase 2 begins, attach `urlcode-auth`,
|
|
266
297
|
`urlcode-admin` and `urlcode-ui` to a session and confirm (a) their actual
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
Status: **decided by the maintainer and implemented in `0.4.0-alpha.2`**
|
|
4
4
|
(`sandbox`/`sandboxReason` in `schemas/urlcode.schema.json`; see the
|
|
5
5
|
`0.4.0-alpha.2` entry in [the roadmap](../ROADMAP.md) and the resulting contract
|
|
6
|
-
in [function security](FUNCTION-SECURITY.md)).
|
|
7
|
-
published to npm. This overturns an
|
|
6
|
+
in [function security](FUNCTION-SECURITY.md)). The release is published; this file retains the decision rationale. This overturns an
|
|
8
7
|
explicit, previously-stated project rule — see "What this reverses" below —
|
|
9
8
|
so treat it as a deliberate, recorded policy change, not a code cleanup. The
|
|
10
9
|
body below is preserved as written at decision time; where it calls downstream
|
|
@@ -153,11 +152,14 @@ with `add_repo` before treating it as settled.
|
|
|
153
152
|
and `peers.json` in `auth`/`admin`/`ui` (`docs/SPIKE-CORE-LAYERING.md`'s
|
|
154
153
|
§2.2 reference) should pin deliberately to a core version that includes
|
|
155
154
|
this change, not inherit it silently on a routine bump.
|
|
156
|
-
- **`urlcode-dynamic-link` (built and published
|
|
155
|
+
- **`urlcode-dynamic-link` (built and published after this was written, then retired, unpublished and deleted in September 2026): moot.** It was a
|
|
157
156
|
mount-based extension like `auth`, not a `function`/`middleware` consumer —
|
|
158
157
|
nothing here changes its design.
|
|
159
|
-
- **`urlcode-middleware` (built and published since
|
|
160
|
-
|
|
158
|
+
- **`urlcode-middleware` (built and published since, then retired, unpublished
|
|
159
|
+
and deleted in September 2026): the rule outlived the package — decided.**
|
|
160
|
+
The decision below was made for that package but is not about it: it governs
|
|
161
|
+
core's own native `middleware:` array, which is where per-route middleware
|
|
162
|
+
lives now that the extension package is gone. First-party middleware is trusted by default, exactly like
|
|
161
163
|
`function`; `sandbox: true` is the same opt-in a developer reaches for
|
|
162
164
|
when a specific `middleware:` wrap genuinely warrants it (e.g. it's
|
|
163
165
|
processing input from a source the developer doesn't fully trust). This
|