@jimhoyd/urlcode 0.4.0-alpha.1 → 0.4.0-alpha.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 +24 -8
- package/.claude/skills/urlcode-operations/SKILL.md +10 -16
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +75 -62
- package/ROADMAP.md +61 -16
- package/SECURITY.md +23 -8
- package/dist/BUILD-MANIFEST.json +38 -43
- package/dist/agents-guide.js +18 -18
- package/dist/build-static.js +134 -0
- package/dist/capabilities.js +85 -31
- package/dist/capability-query.js +0 -1
- package/dist/cli.js +23 -31
- package/dist/compliance-rules/baseline.js +2 -10
- package/dist/compliance-rules/privacy.js +5 -16
- package/dist/compliance-rules/shared.js +0 -2
- package/dist/compliance.js +6 -8
- package/dist/config.js +15 -8
- package/dist/context.js +9 -10
- package/dist/examples.js +2 -2
- package/dist/explain-cli.js +3 -3
- package/dist/explain.js +5 -6
- package/dist/extensions.js +61 -2
- package/dist/function-sources.js +34 -2
- package/dist/function-worker.js +3 -1
- package/dist/functions.js +84 -13
- package/dist/guest-api.js +29 -3
- package/dist/index.js +3 -6
- package/dist/manifest.js +5 -7
- package/dist/mcp.js +1 -1
- package/dist/observability.js +1 -21
- package/dist/policies/cache.js +8 -3
- package/dist/policy.js +28 -9
- package/dist/project-tests.js +3 -3
- package/dist/readiness.js +32 -9
- package/dist/router.js +5 -7
- package/dist/runtime.js +77 -58
- package/dist/sandbox.js +48 -0
- package/dist/scaffold.js +0 -0
- package/dist/scripts/operational-drills.js +12 -54
- package/dist/server.js +3 -29
- package/dist/tooling.js +1 -1
- package/dist/trusted-functions.js +210 -0
- package/dist/types/build-static.d.ts +43 -0
- package/dist/types/capabilities.d.ts +14 -5
- package/dist/types/compliance-rules/shared.d.ts +0 -2
- package/dist/types/compliance.d.ts +0 -3
- package/dist/types/config.d.ts +2 -1
- package/dist/types/context.d.ts +2 -1
- package/dist/types/examples.d.ts +1 -1
- package/dist/types/extensions.d.ts +55 -0
- package/dist/types/function-sources.d.ts +4 -0
- package/dist/types/functions.d.ts +48 -5
- package/dist/types/guest-api.d.ts +1 -0
- package/dist/types/index.d.ts +3 -6
- package/dist/types/manifest.d.ts +1 -3
- package/dist/types/observability.d.ts +1 -14
- package/dist/types/project-tests.d.ts +1 -2
- package/dist/types/readiness.d.ts +10 -3
- package/dist/types/router.d.ts +2 -1
- package/dist/types/runtime.d.ts +0 -27
- package/dist/types/sandbox.d.ts +12 -0
- package/dist/types/scaffold.d.ts +0 -2
- package/dist/types/server.d.ts +1 -4
- package/dist/types/tooling.d.ts +3 -3
- package/dist/types/trusted-functions.d.ts +29 -0
- package/dist/types/types.d.ts +14 -7
- package/dist/types/verify-deployment.d.ts +2 -2
- package/dist/types.js +17 -4
- package/dist/typescript-authoring.js +55 -17
- package/dist/verify-deployment.js +3 -3
- package/docs/AI-AUTHORING.md +104 -9
- package/docs/AWS.md +4 -4
- package/docs/BEST-PRACTICES.md +3 -2
- package/docs/CAPABILITIES.md +29 -16
- package/docs/CAPACITY.md +128 -31
- package/docs/CLOUDFLARE.md +1 -2
- package/docs/COMPLIANCE.md +6 -9
- package/docs/DEPLOYMENT-CHECKS.md +1 -1
- package/docs/EXTENSIONS.md +183 -11
- package/docs/FRAMEWORK.md +35 -19
- package/docs/FUNCTION-SECURITY.md +129 -32
- package/docs/INSTALL.md +0 -5
- package/docs/LOAD-TESTING.md +4 -4
- package/docs/MIDDLEWARE.md +27 -16
- package/docs/MONITORING.md +2 -19
- package/docs/NEXT-PHASE-PLAN.md +12 -4
- package/docs/NEXT-STEPS.md +259 -40
- package/docs/OBSERVABILITY.md +7 -16
- package/docs/OPEN-DECISIONS.md +212 -0
- package/docs/OPERATIONAL-PROOF.md +26 -30
- package/docs/OPERATIONS.md +23 -32
- package/docs/POLICIES.md +15 -4
- package/docs/PRERENDER.md +2 -2
- package/docs/PROJECT-DIRECTION.md +34 -9
- package/docs/README.md +13 -11
- package/docs/RELEASE-READINESS.md +49 -29
- package/docs/RELEASE-SECURITY.md +89 -3
- package/docs/RESILIENCE.md +16 -15
- package/docs/ROUTING.md +8 -10
- package/docs/SANDBOX-REVIEW.md +19 -6
- package/docs/SCAFFOLDING.md +0 -2
- package/docs/SECURITY-AUDIT.md +40 -0
- package/docs/SPECIFICATION.md +60 -26
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +287 -0
- package/docs/SPIKE-BUSINESS-SUITE.md +8 -0
- package/docs/SPIKE-CORE-LAYERING.md +337 -0
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +209 -0
- package/docs/SPIKE-EXTENSION-MODEL.md +2 -2
- package/docs/SPIKE-EXTENSIONS.md +5 -0
- package/docs/SPIKE-LAMBDA-COMPILE.md +4 -2
- package/docs/SPIKE-MONOREPO.md +322 -0
- package/docs/STARTERS.md +1 -1
- package/docs/STATIC.md +94 -0
- package/docs/TOOLING.md +13 -9
- package/docs/TUNNELS.md +0 -3
- package/docs/TYPESCRIPT-AUTHORING.md +27 -12
- package/docs/TYPESCRIPT.md +25 -4
- package/docs/USABILITY-REVIEW.md +7 -1
- package/docs/VERCEL.md +4 -5
- package/docs/VERSION-ALIGNMENT.md +205 -0
- package/docs/YAML-GUIDE.md +0 -3
- package/docs/YAML-REFERENCE.md +16 -6
- package/docs/policies/cache.md +13 -0
- package/examples/assets/example.yaml +1 -1
- package/examples/aws/example.yaml +1 -1
- package/examples/cloudflare/example.yaml +1 -1
- package/examples/conditions/example.yaml +1 -1
- package/examples/cookbook/example.yaml +1 -1
- package/examples/cookbook/middleware/bucket.mjs +12 -2
- package/examples/cookbook/middleware/locale.mjs +7 -3
- package/examples/egress/example.yaml +1 -1
- package/examples/extensions/example.yaml +1 -1
- package/examples/prerender/README.md +2 -2
- package/examples/prerender/example.yaml +1 -1
- package/examples/provider-conformance/example.yaml +1 -1
- package/examples/vercel/example.yaml +1 -1
- package/llms-full.txt +594 -219
- package/llms.txt +23 -12
- package/package.json +10 -3
- package/packaging/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +24 -8
- package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +10 -16
- package/recipes/authenticated-json-api/recipe.yaml +1 -1
- package/recipes/contact-form/recipe.yaml +1 -1
- package/recipes/cors-api/recipe.yaml +1 -1
- package/recipes/health-page/recipe.yaml +1 -1
- package/recipes/json-api/recipe.yaml +1 -1
- package/recipes/middleware/middleware/bucket.mjs +12 -2
- package/recipes/middleware/middleware/locale.mjs +7 -3
- package/recipes/middleware/recipe.yaml +1 -1
- package/recipes/protected-download/recipe.yaml +1 -1
- package/recipes/redirect/recipe.yaml +1 -1
- package/recipes/static-plus-api/recipe.yaml +1 -1
- package/recipes/typescript/README.md +2 -1
- package/recipes/typescript/recipe.yaml +1 -1
- package/recipes/webhook-receiver/README.md +5 -1
- package/recipes/webhook-receiver/recipe.yaml +2 -1
- package/recipes/webhook-receiver/urlcode.yaml +7 -0
- package/schemas/recipe.schema.json +4 -3
- package/schemas/urlcode.schema.json +14 -41
- package/skills/urlcode/SKILL.md +29 -8
- package/starters/default/AGENTS.md +18 -18
- package/starters/default/urlcode.yaml +0 -1
- package/dist/link-api.js +0 -136
- package/dist/link-cli.js +0 -141
- package/dist/link-events.js +0 -76
- package/dist/link-records.js +0 -31
- package/dist/link-store-worker.js +0 -150
- package/dist/link-store.js +0 -250
- package/dist/management-policy.js +0 -40
- package/dist/sqlite-version.js +0 -6
- package/dist/types/link-api.d.ts +0 -30
- package/dist/types/link-cli.d.ts +0 -37
- package/dist/types/link-events.d.ts +0 -27
- package/dist/types/link-records.d.ts +0 -11
- package/dist/types/link-store-worker.d.ts +0 -1
- package/dist/types/link-store.d.ts +0 -130
- package/dist/types/management-policy.d.ts +0 -8
- package/dist/types/sqlite-version.d.ts +0 -1
- package/docs/DYNAMIC-LINKS.md +0 -61
- package/docs/MANAGEMENT-SECURITY.md +0 -82
- package/docs/links/cli.md +0 -110
- package/docs/links/limits.md +0 -175
- package/docs/links/management-api.md +0 -80
- package/docs/links/pools.md +0 -75
- package/docs/links/setup.md +0 -135
- package/docs/yaml/links.md +0 -30
- package/examples/live-links/README.md +0 -11
- package/examples/live-links/example.yaml +0 -21
- package/examples/live-links/tests/requests.json +0 -6
- package/examples/live-links/urlcode.yaml +0 -16
package/docs/OBSERVABILITY.md
CHANGED
|
@@ -33,16 +33,12 @@ named.
|
|
|
33
33
|
| `reload` | `status` `ok`/`rejected`; `version` string and `routes` integer on `ok` | `app.reload()` or the development watcher swapped, or refused to swap, the snapshot. |
|
|
34
34
|
| `watch` | `status` `failed` | The development watcher could not fingerprint the project. |
|
|
35
35
|
| `function_worker` | `status` `started`/`restarting`, `slot` integer; `attempt` and `delayMs` integers on `restarting` | A function worker became ready or is scheduled for replacement. |
|
|
36
|
-
| `link_store_worker` | `status` `started`/`restarting`, `readOnly` boolean; `attempt` and `delayMs` on `restarting` | The same for a link-store connection. |
|
|
37
36
|
| `signal` | `outcome` (`accepted`, `delivered`, `failed`, `dropped`), positive `count` | Best-effort webhook totals; no destination, request data or secrets. |
|
|
38
|
-
| `link_observer` | `status` `failed`/`dropped`/`closed`; `reason` `timeout`/`error` on `failed`; `dropped` integer on `dropped`; `queued`, `delivered`, `dropped`, `failed`, `timedOut`, `closed` on `closed` | Only with `linkEvents`: the link collector failed, the queue shed events, or the channel drained at shutdown. |
|
|
39
|
-
| `link_request` | `requestId`, `collection`, `route`, `code` (only with `includeCode`), `method`, `status`, `outcome`, `durationMs` | Delivered to the `linkEvents.observe` function only, after the response is over. It never reaches the log or observers; its `outcome` is counted in the metrics. |
|
|
40
37
|
| `logs_dropped` | `count` integer | The JSON logger shed records because stdout was not writable. Written by the logger itself, so observers do not see it. |
|
|
41
38
|
| `observer` | `status` `failed`, `name` string | An observer hook threw or rejected. Written to the default log only, never to observers. |
|
|
42
39
|
| `throttle` | `route`, `outcome` `allowed`/`exceeded`, `remaining` integer | A throttle decision. `allowed` is logged only in `mode: report`; enforce mode logs refusals. |
|
|
43
40
|
| `agents` | `route`, `list` string, `outcome` `denied`/`reported` | A User-Agent matched a list. The list name is logged, never the header. |
|
|
44
41
|
| `cache` | `route`, `outcome` `hit`/`stale`/`miss`/`store` | A cache lookup or store. |
|
|
45
|
-
| `management_request` | `timestamp`, `requestId`, `collection`, `action`, `authenticated`, `principal`, `status`, `outcome`, `durationMs` | The link-management API (`startLinkApi`), which has its own logger; `principal` is the operator-configured principal id. |
|
|
46
42
|
| `listening` | `address`, `port`, `mode`, `origin` | Printed once by the CLI at startup, not emitted by the server. |
|
|
47
43
|
|
|
48
44
|
Every event carries `event` (its name). Numbers are JSON numbers, never
|
|
@@ -51,11 +47,10 @@ strings.
|
|
|
51
47
|
### Privacy guarantees
|
|
52
48
|
|
|
53
49
|
No event, snapshot or exposition carries a request URL, path, query string,
|
|
54
|
-
header, body, client address, User-Agent string, binding, secret
|
|
55
|
-
|
|
50
|
+
header, body, client address, User-Agent string, binding, secret or user
|
|
51
|
+
exception text. `route` is always a configured pattern
|
|
56
52
|
from reviewed YAML. `requestId` is server-generated unless
|
|
57
|
-
`--trust-request-id` accepts one from a trusted proxy.
|
|
58
|
-
`link_request` is redacted unless the operator sets `includeCode`. An observer
|
|
53
|
+
`--trust-request-id` accepts one from a trusted proxy. An observer
|
|
59
54
|
that logs should keep the same rule; nothing in an event lets it break it.
|
|
60
55
|
|
|
61
56
|
## Observers
|
|
@@ -98,12 +93,12 @@ call. A hook that throws or returns a rejecting promise is isolated: the
|
|
|
98
93
|
request is unaffected, the next observer still runs, `observers.errors` in
|
|
99
94
|
the snapshot increments and one `observer` record goes to the default log.
|
|
100
95
|
Nothing is retried; an observer that needs delivery guarantees owns its own
|
|
101
|
-
queue
|
|
96
|
+
queue.
|
|
102
97
|
|
|
103
98
|
`onMetrics` receives a fresh snapshot every `metricsIntervalMs`
|
|
104
99
|
(`startServer` option, `0` off by default, 1 s to 1 h) and once at `close()`.
|
|
105
|
-
`onClose` runs in reverse order after the runtime
|
|
106
|
-
|
|
100
|
+
`onClose` runs in reverse order after the runtime has closed.
|
|
101
|
+
`app.observers` lists the `{ name, version }` pairs.
|
|
107
102
|
|
|
108
103
|
`createRuntime(project, { observers })` takes the same array for embedding
|
|
109
104
|
without the server: the runtime's own sink and counters are then yours, and
|
|
@@ -124,7 +119,7 @@ and capped at 10 000 keys.
|
|
|
124
119
|
|
|
125
120
|
| Field | Type | Meaning |
|
|
126
121
|
|---|---|---|
|
|
127
|
-
| `version` | gauge | Snapshot shape version, currently `
|
|
122
|
+
| `version` | gauge | Snapshot shape version, currently `2`. |
|
|
128
123
|
| `uptimeSeconds`, `rssBytes` | gauge | Process facts. |
|
|
129
124
|
| `requests.total`, `requests.byStatusClass.{2xx,3xx,4xx,5xx}` | counter | Application responses, including shed 503s. |
|
|
130
125
|
| `requests.inFlight` | gauge | Requests holding application admission now. |
|
|
@@ -135,13 +130,10 @@ and capped at 10 000 keys.
|
|
|
135
130
|
| `watch.failed` | counter | Development watcher failures. |
|
|
136
131
|
| `functionWorkers.started`, `functionWorkers.restarts` | counter | Worker starts and scheduled replacements. |
|
|
137
132
|
| `functionWorkers.healthySlots`, `functionWorkers.slots` | gauge | Ready slots and configured slots of the serving runtime. |
|
|
138
|
-
| `linkStoreWorkers.started`, `linkStoreWorkers.restarts` | counter | The same for link-store connections. |
|
|
139
133
|
| `policies.throttle.{allowed,exceeded}` | counter | Throttle decisions (see the catalogue for what enforce mode logs). |
|
|
140
134
|
| `policies.agents.{denied,reported}` | counter | Agents decisions. |
|
|
141
135
|
| `policies.cache.{hit,stale,miss,store}` | counter | Cache outcomes. |
|
|
142
|
-
| `linkRequests.{completed,aborted,missing,disabled,expired,invalid_code,invalid_record,unavailable}` | counter | Dynamic link outcomes, counted whether or not `linkEvents` is set. |
|
|
143
136
|
| `signals.{accepted,delivered,failed,dropped}` | counter | Best-effort webhook outcomes; exposed as `signals_total` with outcome labels. |
|
|
144
|
-
| `linkObserver.failed`, `linkObserver.dropped` | counter | Link collector failures and dropped events. |
|
|
145
137
|
| `logsDropped` | counter | Records the JSON logger shed. |
|
|
146
138
|
| `observers.errors` | counter | Observer hooks that threw or rejected. |
|
|
147
139
|
|
|
@@ -171,7 +163,6 @@ urlcode_function_worker_restarts_total 0
|
|
|
171
163
|
urlcode_function_worker_healthy_slots 2
|
|
172
164
|
urlcode_throttle_total{outcome="exceeded"} 14
|
|
173
165
|
urlcode_cache_total{outcome="hit"} 511
|
|
174
|
-
urlcode_link_requests_total{outcome="completed"} 0
|
|
175
166
|
urlcode_logs_dropped_total 0
|
|
176
167
|
urlcode_observer_errors_total 0
|
|
177
168
|
urlcode_uptime_seconds 86400
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Open decisions
|
|
2
|
+
|
|
3
|
+
Status: written 2026-09-19 from a review of the nine repositories. Every item
|
|
4
|
+
here is a maintainer decision that documentation cannot make. Each states what
|
|
5
|
+
is actually true today, what the options are, what it costs to leave open, and
|
|
6
|
+
a recommendation. Nothing here is committed scope; the
|
|
7
|
+
[roadmap](../ROADMAP.md) owns sequence and the
|
|
8
|
+
[readiness register](RELEASE-READINESS.md) owns what is proven.
|
|
9
|
+
|
|
10
|
+
This page exists because several documented positions have drifted apart from
|
|
11
|
+
each other and from the source. The drift is not the decisions themselves — it
|
|
12
|
+
is that they were never recorded in one place where the next contributor, human
|
|
13
|
+
or agent, would look.
|
|
14
|
+
|
|
15
|
+
## 1. Publish `0.4.0-alpha.2` — decided
|
|
16
|
+
|
|
17
|
+
**Decided 2026-09-19: publish `alpha.2`.** The recommendation below was taken
|
|
18
|
+
rather than walking back a correct declaration. The alignment is prepared
|
|
19
|
+
across all nine repositories and the order is recorded in
|
|
20
|
+
[the version register](VERSIONS.md); what remains is merging and tagging.
|
|
21
|
+
|
|
22
|
+
The second hazard was taken with it: `@jimhoyd/urlcode-auth` moves `latest`
|
|
23
|
+
up to `0.1.0-alpha.2` so that a plain `npm install` no longer resolves a build
|
|
24
|
+
older than the floor `@jimhoyd/urlcode-admin` declares. That is a registry
|
|
25
|
+
operation (`npm dist-tag add`), not a source change, and it is listed in the
|
|
26
|
+
release steps.
|
|
27
|
+
|
|
28
|
+
The original entry, for the record:
|
|
29
|
+
|
|
30
|
+
> **Today:** the repository source is `0.4.0-alpha.2`. The npm dist-tags are
|
|
31
|
+
> `latest = 0.3.0` and `alpha = 0.4.0-alpha.1`. `alpha.2` carries the
|
|
32
|
+
> trusted-by-default execution change ([the decision](SPIKE-DEFAULT-TRUST-MODEL.md)).
|
|
33
|
+
>
|
|
34
|
+
> **Why it blocks other things:** `@jimhoyd/urlcode-middleware@0.1.0-alpha.1` is
|
|
35
|
+
> published with `peerDependencies: { "@jimhoyd/urlcode": ">=0.4.0-alpha.2" }`.
|
|
36
|
+
> No published core version satisfies that range, so a registry install of that
|
|
37
|
+
> package cannot resolve its peer. The range is correct; the publication order
|
|
38
|
+
> was not.
|
|
39
|
+
>
|
|
40
|
+
> **Recommendation:** publish `alpha.2`. It is the only option that does not
|
|
41
|
+
> require walking back a correct declaration.
|
|
42
|
+
|
|
43
|
+
## 2. Consolidate the repositories, or commit to the split
|
|
44
|
+
|
|
45
|
+
**Today:** nine repositories. [The monorepo spike](SPIKE-MONOREPO.md) is
|
|
46
|
+
complete, its layout is marked decided (option A), its migration mechanics and
|
|
47
|
+
sequencing are written, and it has three open questions for the maintainer. It
|
|
48
|
+
has been neither accepted nor rejected. It was drafted for four repositories;
|
|
49
|
+
`urlcode-dynamic-link`, `urlcode-middleware` and `urlcode-short` have appeared
|
|
50
|
+
since.
|
|
51
|
+
|
|
52
|
+
**Cost of leaving it open:** the migration gets larger with every repository
|
|
53
|
+
added, and the coordination work the spike describes — pinning reviewed peer
|
|
54
|
+
revisions by hand, chasing prose across repositories when a contract changes —
|
|
55
|
+
is paid again on every contract change in the meantime.
|
|
56
|
+
|
|
57
|
+
**Recommendation:** decide before the next structural change, in either
|
|
58
|
+
direction. A recorded "no, and here is what we do instead about cross-repo
|
|
59
|
+
drift" closes this as well as a yes does.
|
|
60
|
+
|
|
61
|
+
## 3. One way to attach middleware, or two
|
|
62
|
+
|
|
63
|
+
**Today:** core has the native `routes.<path>.middleware[]` array.
|
|
64
|
+
`urlcode-middleware` provides the same capability through the extension seam
|
|
65
|
+
and states that core's native array "keeps working unchanged".
|
|
66
|
+
[The layering spike](SPIKE-CORE-LAYERING.md) proposed *extracting* middleware
|
|
67
|
+
to narrow core to YAML plus `function`; what exists is an addition alongside it.
|
|
68
|
+
|
|
69
|
+
**Why this is a principle question:** the
|
|
70
|
+
[extension model review](SPIKE-EXTENSION-MODEL.md) rejected an earlier design
|
|
71
|
+
for exactly this reason — two ways to protect a route would drift apart — and
|
|
72
|
+
resolved it by removing one. The same test applies here.
|
|
73
|
+
|
|
74
|
+
**Options:** deprecate the native array on a stated timeline and complete the
|
|
75
|
+
extraction; or keep both and document precisely when each is correct, treating
|
|
76
|
+
the extension as a bounded variant rather than a successor.
|
|
77
|
+
|
|
78
|
+
**Recommendation:** decide explicitly and write the answer into both
|
|
79
|
+
repositories. Either is defensible; leaving both undescribed is not.
|
|
80
|
+
|
|
81
|
+
## 4. Where the backlog lives
|
|
82
|
+
|
|
83
|
+
**Today:** [next steps](NEXT-STEPS.md) holds roughly thirty tracked items
|
|
84
|
+
across nine phases. The repository has one open issue (#58). `AGENTS.md`
|
|
85
|
+
instructs contributors and agents to file what they find as issues on the
|
|
86
|
+
owning repository.
|
|
87
|
+
|
|
88
|
+
**The contradiction:** the instruction points at the issue tracker; the actual
|
|
89
|
+
plan is a Markdown file that no tracker reflects. A plan document also has no
|
|
90
|
+
state, so items stay written as future work after they ship — which is how
|
|
91
|
+
route-level `auth` came to be described as an invented field in merged agent
|
|
92
|
+
guidance (see item 7).
|
|
93
|
+
|
|
94
|
+
**Options:** move the phase items into issues and keep the document as the
|
|
95
|
+
narrative that links them; or keep the document as the system of record and
|
|
96
|
+
amend `AGENTS.md` to say so.
|
|
97
|
+
|
|
98
|
+
**Recommendation:** issues for items, document for sequence. It is the only
|
|
99
|
+
arrangement where "done" is recorded automatically.
|
|
100
|
+
|
|
101
|
+
## 5. Gate the business suite on evidence
|
|
102
|
+
|
|
103
|
+
**Today:** [the business suite spike](SPIKE-BUSINESS-SUITE.md) proposes seven
|
|
104
|
+
applications. [Project direction](PROJECT-DIRECTION.md) states the evidence
|
|
105
|
+
test: the framework grows from measured repetition, not from a list of things
|
|
106
|
+
applications might need. Phase 6 of [next steps](NEXT-STEPS.md) says candidate
|
|
107
|
+
areas are built only when the repetition log shows them repeating. The
|
|
108
|
+
repetition log does not exist yet, and the Phase 0 agent benchmark has not been
|
|
109
|
+
run.
|
|
110
|
+
|
|
111
|
+
**Recommendation:** record on the spike itself that it is gated behind the
|
|
112
|
+
benchmark and the repetition log, or amend the evidence test. Holding both
|
|
113
|
+
positions unannotated makes the stated principle decorative.
|
|
114
|
+
|
|
115
|
+
## 6. Finish the documentation move, or describe the split
|
|
116
|
+
|
|
117
|
+
**Today:** `AGENTS.md` states that urlcode-docs is the documentation home and
|
|
118
|
+
the only place readers are sent, that new reader-facing pages go there, and
|
|
119
|
+
that pages still under `docs/` here are being migrated. Roughly forty
|
|
120
|
+
reader-facing pages remain in this repository, several duplicated in
|
|
121
|
+
urlcode-docs.
|
|
122
|
+
|
|
123
|
+
**Observed consequence:** the same fact drifts between copies. The public
|
|
124
|
+
documentation site carried the correct trusted-by-default contract while
|
|
125
|
+
`docs/OPERATIONS.md` in this repository still described functions as untrusted
|
|
126
|
+
and isolated by default. <!-- trust-model-prose: historical -->
|
|
127
|
+
|
|
128
|
+
**Options:** finish the migration against a dated list; or state which pages
|
|
129
|
+
stay here permanently and why, and add a check that fails when a new
|
|
130
|
+
reader-facing page appears beside them.
|
|
131
|
+
|
|
132
|
+
## 7. A review window, and a check that does not need one
|
|
133
|
+
|
|
134
|
+
**Today:** [governance](../GOVERNANCE.md) records one maintainer, a required
|
|
135
|
+
approval count of zero, and that "an independent human review is not yet
|
|
136
|
+
guaranteed". Pull requests and CI are mandatory; a second pair of eyes is not.
|
|
137
|
+
|
|
138
|
+
**What that permitted, concretely:** on 2026-09-19, three coordinated pull
|
|
139
|
+
requests (urlcode#158, urlcode-template#6, urlcode-docs#17) opened and merged
|
|
140
|
+
within nine minutes. They recorded a genuine improvement — the declarative-first
|
|
141
|
+
principle, propagated to every copy in one change — and alongside it the
|
|
142
|
+
statement that agents must "never invent an `auth` field". Route-level `auth` is
|
|
143
|
+
implemented: it is defined in `schemas/urlcode.schema.json` as a short form
|
|
144
|
+
expanding to `policies.extensions.auth`, expanded in `src/config.ts`, typed in
|
|
145
|
+
`src/types.ts`, and asserted in `test/recipes.test.ts`. The guidance now
|
|
146
|
+
instructs agents away from a supported declarative short form and toward the
|
|
147
|
+
lower-level policy form — the opposite of the principle the same change
|
|
148
|
+
introduced — in the generated project guide, the bundled starter, both
|
|
149
|
+
authoring skills, the packaged plugin skill and the two generated `llms` files.
|
|
150
|
+
|
|
151
|
+
Nothing failed. Lint, typecheck, generated-resource checks, the package smoke
|
|
152
|
+
test and 501 tests all passed, because no check compares what the guidance
|
|
153
|
+
claims against what the schema implements.
|
|
154
|
+
|
|
155
|
+
**The check, now implemented.** `scripts/check-guidance-claims.ts` runs inside
|
|
156
|
+
`npm run check` and exits non-zero on a contradiction between agent-facing
|
|
157
|
+
guidance and the schema, so this class of error fails CI instead of depending on
|
|
158
|
+
a reviewer's attention:
|
|
159
|
+
|
|
160
|
+
- Inputs: the agent-facing surfaces — `src/agents-guide.ts`,
|
|
161
|
+
`starters/default/AGENTS.md`, `skills/urlcode/SKILL.md`,
|
|
162
|
+
`.claude/skills/*/SKILL.md`, `packaging/claude-plugin/skills/*/SKILL.md`,
|
|
163
|
+
`llms.txt`, `llms-full.txt`, `docs/AI-AUTHORING.md`.
|
|
164
|
+
- Assertion one: every YAML field named as valid in those files resolves in
|
|
165
|
+
`schemas/urlcode.schema.json`. A guidance file may not teach a field the
|
|
166
|
+
schema does not accept.
|
|
167
|
+
- Assertion two, the one that would have caught this: no field that the schema
|
|
168
|
+
*does* define is described as unsupported, invented or nonexistent. Match the
|
|
169
|
+
negative phrasings deliberately ("never invent", "does not exist",
|
|
170
|
+
"unsupported field") within a short window of a schema-resolvable field name.
|
|
171
|
+
- Assertion three: handlers listed as available match the capability catalog,
|
|
172
|
+
so a removed handler (`link`, extracted to `urlcode-dynamic-link` in
|
|
173
|
+
`f7dbe54`) cannot linger in generated guidance.
|
|
174
|
+
- Exits non-zero on violation, with a documented `<!-- guidance-claims: ignore -->`
|
|
175
|
+
marker for text that is deliberately about another version. Verified against
|
|
176
|
+
both regressions: reintroducing the "never invent an `auth` field" sentence
|
|
177
|
+
fails the check, and adding the removed `link` handler to the inventory line
|
|
178
|
+
fails it.
|
|
179
|
+
|
|
180
|
+
What remains a decision: whether `npm run check` membership is enough, or the
|
|
181
|
+
check should also be named in the repository's required status checks so it
|
|
182
|
+
cannot be bypassed.
|
|
183
|
+
|
|
184
|
+
**Recommendation:** the check is in; keep it required. It is the part that does
|
|
185
|
+
not depend on a person being available. Whether to
|
|
186
|
+
also raise the required approval count is a separate call, and
|
|
187
|
+
[governance](../GOVERNANCE.md) already states the condition — when the trusted
|
|
188
|
+
maintainer team grows.
|
|
189
|
+
|
|
190
|
+
## 8. One publishing convention
|
|
191
|
+
|
|
192
|
+
**Today:** `urlcode-dynamic-link` and `urlcode-middleware` keep
|
|
193
|
+
`"private": true` on their main branch and drop it in the release commit;
|
|
194
|
+
`urlcode-auth`, `urlcode-admin` and `urlcode-ui` do not. All are published.
|
|
195
|
+
Both practices are defensible; having both means the flag no longer indicates
|
|
196
|
+
publication state.
|
|
197
|
+
|
|
198
|
+
**Recommendation:** pick one and state it where release process is documented.
|
|
199
|
+
|
|
200
|
+
## 9. Adjudicate the vendored skill drift
|
|
201
|
+
|
|
202
|
+
**Today:** `npm run check:downstream-skills` reports that `urlcode-template`'s
|
|
203
|
+
vendored authoring and operations skills differ from core's current `main` by
|
|
204
|
+
79 and 91 lines. The report is advisory by design and never fails, because a
|
|
205
|
+
downstream repository can correctly pin an older published core version
|
|
206
|
+
([issue 155](https://github.com/jimhoyd-com/urlcode/issues/155)).
|
|
207
|
+
|
|
208
|
+
**What is missing:** the judgment the report defers. No one has read the two
|
|
209
|
+
diffs and recorded which side is right for the template's `0.4.0-alpha.1` pin.
|
|
210
|
+
|
|
211
|
+
**Recommendation:** review both diffs once, record the verdict, and re-run the
|
|
212
|
+
report when the template repins.
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
# Operational evidence and deployment acceptance
|
|
2
2
|
|
|
3
|
-
`node scripts/operational-drills.ts` creates and deletes its own temporary
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
`node scripts/operational-drills.ts` creates and deletes its own temporary
|
|
4
|
+
project. It runs mixed real HTTP requests against native redirects and
|
|
5
|
+
`function` routes; asserts responses; rejects a bad reload; and activates and
|
|
6
|
+
rolls back a configuration. `URLCODE_SOAK_SECONDS=60` selects a longer run
|
|
7
|
+
(1–3600 seconds, default 5). Output is JSON with request count, batch p99
|
|
8
|
+
duration and RSS. Batch latency is not per-request p99 or a capacity promise.
|
|
9
|
+
CI's `verify` job runs the short drill across a 3-Node (22/24/26) × 3-OS
|
|
10
|
+
(Linux/macOS/Windows) matrix, nine combinations, but only on a push to `main`.
|
|
11
|
+
A pull request runs the same drill across all three Node versions on Linux
|
|
12
|
+
only (3 of the 9 combinations); the macOS/Windows legs only run once a PR
|
|
13
|
+
merges, per `.github/workflows/ci.yml`'s matrix.
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Never point this option at production storage: it intentionally consumes up to
|
|
18
|
-
84 MiB of writes in a newly created temporary child directory. The CI mount is
|
|
19
|
-
nonpersistent. An I/O device failure or power cut is a different failure mode.
|
|
15
|
+
Core has no durable store of its own, so this drill has no backup/restore or
|
|
16
|
+
disk-exhaustion exercise: a future extension package owning durable state
|
|
17
|
+
(such as the planned `urlcode-dynamic-link`) is responsible for its own
|
|
18
|
+
persistence proof once it exists.
|
|
20
19
|
|
|
21
20
|
## Required proof on the intended deployment
|
|
22
21
|
|
|
@@ -27,19 +26,16 @@ substitute for it. The deployment owner must record:
|
|
|
27
26
|
|
|
28
27
|
| Exercise | Acceptance evidence |
|
|
29
28
|
|---|---|
|
|
30
|
-
| Soak | At least 24 hours at expected peak and burst load through actual TLS/ingress; native/function
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
| Soak | At least 24 hours at expected peak and burst load through actual TLS/ingress; native/function mix, slow clients, response correctness, p50/p95/p99, throughput, error/rejection rate, RSS plateau, CPU and FD growth. Define numerical SLOs before starting |
|
|
30
|
+
| Kill/restart | Kill the server with outstanding requests. Reconcile uncertain mutations by request ID, restore readiness, and prove no duplicate side effects from application code |
|
|
31
|
+
| Rollback | Deploy candidate by exact digest beside last-good, run route assertions, switch ingress, drain, then switch back. Verify configuration/policy compatibility |
|
|
32
|
+
| Monitoring | Deliver test alerts for missing logs, sustained errors, pool rejection/failure, low disk, restart storms and failed readiness to a named on-call owner |
|
|
33
|
+
|
|
34
|
+
If your deployment adds a durable-state extension, add that extension's own
|
|
35
|
+
backup/restore, logical export/import and disk/log exhaustion exercises to
|
|
36
|
+
this table; core's proof above does not cover them.
|
|
37
37
|
|
|
38
38
|
Record date, operator, source/app/policy/image digests, topology, hardware/limits,
|
|
39
|
-
commands, duration, synthetic dataset size, raw metrics/log locations, result
|
|
40
|
-
|
|
41
|
-
record is not a pass.
|
|
42
|
-
The executable local drill uses a fully quiesced, explicitly checkpointed database intentionally. A last
|
|
43
|
-
read-only connection can leave WAL frames even after every connection closes;
|
|
44
|
-
closing alone is insufficient proof that the main database file is a full backup. For a
|
|
45
|
-
live-backup system, prove its SQLite-consistent snapshot method separately.
|
|
39
|
+
commands, duration, synthetic dataset size, raw metrics/log locations, result
|
|
40
|
+
and unresolved findings. A reviewer signs the acceptance record; a blank
|
|
41
|
+
record is not a pass.
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -45,8 +45,10 @@ docker run --rm --name my-links \
|
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
Replace the example mount with your app. The image uses the unprivileged `node`
|
|
48
|
-
user; ensure mounted config/functions are readable by it.
|
|
49
|
-
|
|
48
|
+
user; ensure mounted config/functions are readable by it. Core has no writable
|
|
49
|
+
mount of its own; a future mount-based extension (like `auth`/`admin`, see
|
|
50
|
+
[extensions](EXTENSIONS.md)) is the place for operator-owned writable state.
|
|
51
|
+
Sandboxed application functions cannot access mounted files or installed
|
|
50
52
|
Node packages. The resource values above illustrate
|
|
51
53
|
container limits, not a sizing recommendation; large configuration compilation
|
|
52
54
|
can need more memory. Measure your workload. Tag/redeploy immutable image digests
|
|
@@ -63,7 +65,10 @@ at the proxy; they are unauthenticated and reveal route count/config digest.
|
|
|
63
65
|
|
|
64
66
|
If functions perform sensitive actions, implement authentication and authorization
|
|
65
67
|
in the application. A short URL is not automatically an access-control mechanism.
|
|
66
|
-
Functions
|
|
68
|
+
Functions and middleware run trusted and unsandboxed by default, in the host
|
|
69
|
+
process with full Node, filesystem and network access; a route that declares
|
|
70
|
+
`sandbox: true` runs isolated in QuickJS/WebAssembly instead (see
|
|
71
|
+
[function security](FUNCTION-SECURITY.md)). Keep separate deployment
|
|
67
72
|
processes/containers and narrowly scoped credentials as additional boundaries.
|
|
68
73
|
Do not expose a public code-upload/multi-tenant service on the basis of the self-hosted release alone
|
|
69
74
|
without separate security review and stronger service-level containment.
|
|
@@ -89,14 +94,14 @@ production does not watch or refresh secret values automatically.
|
|
|
89
94
|
|
|
90
95
|
- `GET /_urlcode/health`: process liveness.
|
|
91
96
|
- `GET /_urlcode/ready`: 200 when the active snapshot and all function workers
|
|
92
|
-
are available
|
|
97
|
+
are available; 503 while a worker
|
|
93
98
|
is unavailable. Busy workers alone do not
|
|
94
|
-
mark readiness down. A failed worker
|
|
99
|
+
mark readiness down. A failed worker is replaced with
|
|
95
100
|
exponential backoff (250 ms doubling to a 30-second ceiling) and readiness
|
|
96
101
|
reports 503 until every slot is serving again. Replacement does not stop, so a
|
|
97
102
|
request-triggered deadline cannot disable functions until an operator restarts;
|
|
98
103
|
a cause that keeps recurring keeps the instance shedding load and needs an
|
|
99
|
-
operator. Alert on sustained `function_worker
|
|
104
|
+
operator. Alert on sustained `function_worker` restart events.
|
|
100
105
|
- Probes are answered from their own admission budget (16 by default,
|
|
101
106
|
`--max-in-flight-health`), so they stay available while the application is
|
|
102
107
|
saturated without being an unmetered endpoint. They are unauthenticated and
|
|
@@ -125,11 +130,15 @@ production does not watch or refresh secret values automatically.
|
|
|
125
130
|
admitted through response completion; excess requests receive 503. Health probes
|
|
126
131
|
remain available under admission saturation. A 15-second socket inactivity
|
|
127
132
|
timeout closes stalled readers/writers. Proxy timeouts/rate limits still matter.
|
|
128
|
-
- Functions: 2 concurrent workers (`--workers`),
|
|
129
|
-
(`--function-timeout-ms`)
|
|
130
|
-
|
|
133
|
+
- Functions: a `sandbox: true` route gets 2 concurrent workers (`--workers`),
|
|
134
|
+
no queue and a 5-second deadline (`--function-timeout-ms`); a trusted route
|
|
135
|
+
(`sandbox` false or absent, the default) shares the in-flight admission cap
|
|
136
|
+
instead of a worker pool and races the same deadline. Either mode buffers
|
|
137
|
+
1 MiB of response (`--max-response-bytes`) and 16 KiB response headers.
|
|
138
|
+
Saturation 503; timeout 504; error 502.
|
|
131
139
|
QuickJS guests have a 32 MiB heap and 512 KiB stack budget and no network or
|
|
132
|
-
host capabilities
|
|
140
|
+
host capabilities; a trusted route has neither budget and full Node access.
|
|
141
|
+
Outer workers have additional V8 limits. Total process/WASM
|
|
133
142
|
memory still needs deployment-level limits; do not equate guest budget with RSS.
|
|
134
143
|
|
|
135
144
|
`urlcode serve`/`dev` and the JavaScript server API both configure workers,
|
|
@@ -140,24 +149,14 @@ deployment controls, not portable route behavior. Horizontal replicas
|
|
|
140
149
|
must use identical application/config versions and secret bindings. In-memory
|
|
141
150
|
function state is reset after every invocation, not durable/shared application state.
|
|
142
151
|
General application storage needs a future explicit capability broker; no
|
|
143
|
-
storage/network access is exposed to the guest.
|
|
144
|
-
|
|
152
|
+
storage/network access is exposed to the guest. Stored short links are moving
|
|
153
|
+
to a future `urlcode-dynamic-link` extension package, not yet published; core
|
|
154
|
+
no longer has a native link store.
|
|
145
155
|
|
|
146
156
|
The health version combines route-definition and asset-representation digests;
|
|
147
157
|
it does not identify the complete function/runtime release. Record runtime commit,
|
|
148
158
|
application commit, dependency locks and image digest in your deployment system.
|
|
149
159
|
|
|
150
|
-
## Optional dynamic-link deployment
|
|
151
|
-
|
|
152
|
-
Keep SQLite and management tokens outside the application, in a private durable
|
|
153
|
-
local directory. Initialize through `links init/create`, bind public serving with
|
|
154
|
-
`--link-store`, and expose management on a separate private listener. Restrict
|
|
155
|
-
its token to your trusted backend; apply ingress limits and backups. Public
|
|
156
|
-
serving opens read-only pools; management has a separate writer and read pool.
|
|
157
|
-
Budget connections across processes and monitor writer health separately. See
|
|
158
|
-
[dynamic-link operations](DYNAMIC-LINKS.md). Multiple host replicas must not share
|
|
159
|
-
this file over a network filesystem; no distributed adapter is included yet.
|
|
160
|
-
|
|
161
160
|
## Deployment and rollback procedure
|
|
162
161
|
|
|
163
162
|
1. Build a candidate from pinned runtime/application revisions and lockfiles.
|
|
@@ -174,7 +173,7 @@ this file over a network filesystem; no distributed adapter is included yet.
|
|
|
174
173
|
This is an operator procedure, not an implemented deployment control plane.
|
|
175
174
|
Rollback cannot undo a function's external side effects or migrate an app's
|
|
176
175
|
state automatically. Plan those independently. Keep Git definitions backed up;
|
|
177
|
-
back up any app-owned persistent state separately. YAML routes require no database
|
|
176
|
+
back up any app-owned persistent state separately. YAML routes require no database.
|
|
178
177
|
|
|
179
178
|
## Capacity and incident planning
|
|
180
179
|
|
|
@@ -200,11 +199,3 @@ adapter is made by the current release.
|
|
|
200
199
|
The [2026-09-16 internal audit](SECURITY-AUDIT.md) records fixes, regression evidence
|
|
201
200
|
and remaining security/operational gates. This is not an independent assessment.
|
|
202
201
|
|
|
203
|
-
## Management hardening baseline
|
|
204
|
-
|
|
205
|
-
Management is now restricted to literal loopback addresses. Prefer `--auth-file`
|
|
206
|
-
for individual expiring, revocable credentials with collection/action scopes.
|
|
207
|
-
Every successful built-in store mutation has an atomic, durable SQLite audit row;
|
|
208
|
-
HTTP request logs remain best effort. See [management security](MANAGEMENT-SECURITY.md)
|
|
209
|
-
for policy examples, compatibility, archival and rollback requirements, and
|
|
210
|
-
[operational proof](OPERATIONAL-PROOF.md) for executable recovery drills.
|
package/docs/POLICIES.md
CHANGED
|
@@ -40,11 +40,22 @@ routes:
|
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
`policies` at the top level sets project defaults; `routes.<path>.policies`
|
|
43
|
-
adjusts them for one route.
|
|
43
|
+
adjusts them for one route. Two route-level short forms exist. `auth`
|
|
44
44
|
expands to `policies.extensions.auth` when the project declares an auth
|
|
45
|
-
[extension](EXTENSIONS.md).
|
|
46
|
-
`cache
|
|
47
|
-
|
|
45
|
+
[extension](EXTENSIONS.md). `cache: {strategy, maxAge, ...}` expands to
|
|
46
|
+
`policies.cache` the same way — the compiler merges it into that route's
|
|
47
|
+
`policies` before anything else reads the project, so `routes`, `audit` and
|
|
48
|
+
`explain` see only the expanded long form, and it is refused alongside a
|
|
49
|
+
direct `policies.cache` on the same route (use one form):
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
routes:
|
|
53
|
+
/feed:
|
|
54
|
+
function: { source: functions/feed.mjs }
|
|
55
|
+
cache: { strategy: swr, maxAge: 30 } # expands to policies: { cache: {...} }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Both accept the same keys: `profile` plus one entry
|
|
48
59
|
per policy, each either an object or `false`. Unknown keys fail validation, as
|
|
49
60
|
everywhere in the project format. The
|
|
50
61
|
[field reference](YAML-REFERENCE.md) lists every accepted field with its
|
package/docs/PRERENDER.md
CHANGED
|
@@ -14,7 +14,7 @@ and no capability.
|
|
|
14
14
|
## Why render ahead of time
|
|
15
15
|
|
|
16
16
|
A function response body is readable by middleware through `text()`. A native
|
|
17
|
-
`page`, `static`, `download`, `redirect
|
|
17
|
+
`page`, `static`, `download`, `redirect` or `respond` body is not — that
|
|
18
18
|
opacity is a deliberate contract, not a gap, and it is what keeps native file
|
|
19
19
|
serving free of guest code. See [middleware](MIDDLEWARE.md).
|
|
20
20
|
|
|
@@ -165,7 +165,7 @@ service and redirect; provider asset adapters are not implemented.
|
|
|
165
165
|
|
|
166
166
|
The first two rows above are the sandbox's snapshot budgets: at most 127 guest
|
|
167
167
|
modules and 4 MiB of module source in one snapshot. They are deliberate — part
|
|
168
|
-
of what [function security](FUNCTION-SECURITY.md) promises about
|
|
168
|
+
of what [function security](FUNCTION-SECURITY.md) promises about sandboxed guest
|
|
169
169
|
code — and the render step does not relax them for trusted generated content.
|
|
170
170
|
Serving a project that crosses either still fails at startup, naming the module
|
|
171
171
|
that crossed it:
|
|
@@ -6,12 +6,31 @@ under. It describes intent and
|
|
|
6
6
|
boundaries, not a delivery schedule; the [roadmap](../ROADMAP.md) owns sequence
|
|
7
7
|
and the [readiness register](RELEASE-READINESS.md) owns what is proven.
|
|
8
8
|
|
|
9
|
+
## Design principle: declarative-first
|
|
10
|
+
|
|
11
|
+
> Use URLCode's highest-level declarative features whenever possible. Generate custom code only when the framework cannot express the requirement.
|
|
12
|
+
|
|
13
|
+
This is the default for human authors, coding agents, examples and framework
|
|
14
|
+
changes. Before writing application code, check the installed version's declarative
|
|
15
|
+
primitives, YAML configuration, policies, supported extensions and reusable recipes
|
|
16
|
+
or templates. Prefer the highest-level supported abstraction that satisfies the
|
|
17
|
+
requirement; do not recreate framework behavior in a lower-level handler.
|
|
18
|
+
|
|
19
|
+
Custom functions or middleware remain valid for application-specific behavior the
|
|
20
|
+
framework cannot express. Keep that code focused, explain the missing capability,
|
|
21
|
+
and report reusable gaps to the owning repository. Never invent YAML fields or
|
|
22
|
+
bypass target limits, sandbox isolation or operator grants to avoid custom code.
|
|
23
|
+
|
|
9
24
|
## What URLCode is
|
|
10
25
|
|
|
11
26
|
A portable runtime for programmable URL behavior. A project declares its public
|
|
12
|
-
URL surface in YAML, adds
|
|
27
|
+
URL surface in YAML, adds JavaScript only where declarative handlers are
|
|
13
28
|
not enough, and runs the same definition locally, in a container, or on operator
|
|
14
|
-
infrastructure.
|
|
29
|
+
infrastructure. That JavaScript runs trusted, in the host process, like any
|
|
30
|
+
other project code, unless the project isolates a specific route with
|
|
31
|
+
`sandbox: true` (docs/SPIKE-DEFAULT-TRUST-MODEL.md) — a judgment call the
|
|
32
|
+
project makes per route, not a default the runtime imposes on all guest code.
|
|
33
|
+
The project format is deliberately bounded so a runtime can
|
|
15
34
|
validate it, inspect it, test it and eventually carry it across hosting providers.
|
|
16
35
|
|
|
17
36
|
Git owns route definitions and code. Operators own credentials, storage and
|
|
@@ -19,12 +38,18 @@ capability grants. Application data stays in the operator's systems.
|
|
|
19
38
|
|
|
20
39
|
## What URLCode is not
|
|
21
40
|
|
|
22
|
-
- **Not a URL shortener.**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
- **Not a URL shortener.** Core is redirects, validated responses, request
|
|
42
|
+
functions, middleware, pages, static assets and downloads. Stored short
|
|
43
|
+
links are moving to a future `urlcode-dynamic-link` extension package
|
|
44
|
+
(mount-based, like `auth`/`admin`, not yet published), not a core handler.
|
|
45
|
+
- **Not a general Node web framework.** There is no framework code to write
|
|
46
|
+
for routing, validation, middleware wiring or policies — those are declared
|
|
47
|
+
in YAML and enforced by the runtime. Function/middleware code that needs
|
|
48
|
+
isolation from the host (untrusted input, an unreviewed contribution, a
|
|
49
|
+
particularly sensitive secret) opts into `sandbox: true`, which runs it
|
|
50
|
+
inside WASM isolation with no ambient filesystem, network or Node APIs.
|
|
51
|
+
Behavior that cannot be expressed in the bounded contract of a sandboxed
|
|
52
|
+
route is rejected rather than emulated.
|
|
28
53
|
- **Not a hosting account system.** There is no end-user identity, billing or
|
|
29
54
|
public account surface. Management is a private operator API.
|
|
30
55
|
- **Not a provider configuration format.** Provider infrastructure settings do
|
|
@@ -54,7 +79,7 @@ and the database owns the machinery. One level up, a route should read
|
|
|
54
79
|
|
|
55
80
|
```yaml
|
|
56
81
|
/admin:
|
|
57
|
-
auth: {
|
|
82
|
+
auth: { role: admin }
|
|
58
83
|
function: { source: functions/admin.mjs }
|
|
59
84
|
```
|
|
60
85
|
|