@jimhoyd/urlcode 0.3.0 → 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 +122 -0
- package/.claude/skills/urlcode-operations/SKILL.md +108 -0
- package/.claude-plugin/marketplace.json +18 -0
- package/CONTRIBUTING.md +30 -2
- package/README.md +195 -255
- package/ROADMAP.md +143 -15
- package/SECURITY.md +31 -9
- package/dist/BUILD-MANIFEST.json +72 -47
- package/dist/adapters.js +4 -23
- package/dist/agent-lists.js +1 -1
- package/dist/agents-guide.js +113 -0
- package/dist/authoring-files.js +60 -0
- package/dist/authoring.js +11 -1
- package/dist/aws.js +4 -3
- package/dist/build-cloudflare.js +11 -24
- package/dist/build-static.js +134 -0
- package/dist/bulk.js +37 -0
- package/dist/capabilities.js +262 -0
- package/dist/capability-query.js +71 -0
- package/dist/catalog.js +105 -0
- package/dist/cli.js +165 -34
- package/dist/client-address.js +1 -1
- package/dist/compliance-rules/baseline.js +9 -17
- package/dist/compliance-rules/privacy.js +7 -18
- package/dist/compliance-rules/shared.js +0 -2
- package/dist/compliance-rules/strict.js +5 -5
- package/dist/compliance.js +6 -8
- package/dist/conditions.js +88 -0
- package/dist/config.js +69 -6
- package/dist/context.js +155 -0
- package/dist/ecosystem-cli.js +88 -0
- package/dist/egress.js +98 -0
- package/dist/examples.js +92 -0
- package/dist/explain-cli.js +64 -0
- package/dist/explain.js +131 -0
- package/dist/extensions.js +231 -0
- package/dist/function-sources.js +49 -5
- package/dist/function-worker.js +3 -1
- package/dist/functions.js +84 -13
- package/dist/guest-api.js +29 -3
- package/dist/index.js +40 -6
- package/dist/init-with.js +165 -0
- package/dist/interchange-cli.js +42 -0
- package/dist/interchange.js +189 -0
- package/dist/manifest.js +109 -0
- package/dist/match.js +2 -2
- package/dist/mcp-authoring.js +147 -0
- package/dist/mcp.js +97 -0
- package/dist/observability.js +7 -21
- package/dist/operator-host.js +29 -0
- package/dist/plugins.js +12 -0
- package/dist/policies/agents.js +2 -2
- package/dist/policies/cache.js +8 -3
- package/dist/policies/compression.js +2 -1
- package/dist/policies/security.js +0 -0
- package/dist/policies.js +1 -1
- package/dist/policy.js +56 -15
- package/dist/prerender.js +100 -41
- package/dist/project-tests.js +3 -3
- package/dist/provider-verification.js +92 -0
- package/dist/proxy.js +44 -0
- package/dist/readiness.js +34 -11
- package/dist/recipes.js +41 -0
- package/dist/route-diff.js +106 -0
- package/dist/router.js +45 -7
- package/dist/runtime.js +164 -64
- package/dist/sandbox.js +48 -0
- package/dist/scaffold.js +0 -0
- package/dist/schema-query.js +62 -0
- package/dist/scripts/operational-drills.js +12 -54
- package/dist/server.js +3 -29
- package/dist/signals.js +24 -0
- package/dist/site.js +0 -0
- package/dist/tooling.js +96 -0
- package/dist/trusted-functions.js +210 -0
- package/dist/types/adapters.d.ts +7 -4
- package/dist/types/agent-lists.d.ts +0 -1
- package/dist/types/agents-guide.d.ts +17 -0
- package/dist/types/authoring-files.d.ts +10 -0
- package/dist/types/aws.d.ts +3 -1
- package/dist/types/build-cloudflare.d.ts +1 -0
- package/dist/types/build-static.d.ts +43 -0
- package/dist/types/bulk.d.ts +27 -0
- package/dist/types/capabilities.d.ts +64 -0
- package/dist/types/capability-query.d.ts +24 -0
- package/dist/types/catalog.d.ts +65 -0
- package/dist/types/client-address.d.ts +0 -1
- package/dist/types/compliance-rules/baseline.d.ts +1 -9
- package/dist/types/compliance-rules/privacy.d.ts +1 -4
- package/dist/types/compliance-rules/shared.d.ts +0 -2
- package/dist/types/compliance-rules/strict.d.ts +0 -5
- package/dist/types/compliance.d.ts +0 -3
- package/dist/types/conditions.d.ts +19 -0
- package/dist/types/config.d.ts +21 -2
- package/dist/types/context.d.ts +66 -0
- package/dist/types/ecosystem-cli.d.ts +17 -0
- package/dist/types/egress.d.ts +46 -0
- package/dist/types/examples.d.ts +50 -0
- package/dist/types/explain-cli.d.ts +11 -0
- package/dist/types/explain.d.ts +95 -0
- package/dist/types/extensions.d.ts +177 -0
- package/dist/types/function-sources.d.ts +9 -0
- package/dist/types/functions.d.ts +48 -5
- package/dist/types/guest-api.d.ts +1 -0
- package/dist/types/index.d.ts +36 -6
- package/dist/types/init-with.d.ts +30 -0
- package/dist/types/interchange-cli.d.ts +16 -0
- package/dist/types/interchange.d.ts +42 -0
- package/dist/types/manifest.d.ts +79 -0
- package/dist/types/match.d.ts +1 -0
- package/dist/types/mcp-authoring.d.ts +92 -0
- package/dist/types/mcp.d.ts +12 -0
- package/dist/types/observability.d.ts +3 -14
- package/dist/types/operator-host.d.ts +8 -0
- package/dist/types/plugins.d.ts +2 -0
- package/dist/types/policies/agents.d.ts +0 -2
- package/dist/types/policies/compression.d.ts +2 -0
- package/dist/types/policies/security.d.ts +0 -1
- package/dist/types/policy.d.ts +15 -4
- package/dist/types/project-tests.d.ts +3 -2
- package/dist/types/provider-verification.d.ts +53 -0
- package/dist/types/proxy.d.ts +21 -0
- package/dist/types/readiness.d.ts +10 -3
- package/dist/types/recipes.d.ts +30 -0
- package/dist/types/route-diff.d.ts +27 -0
- package/dist/types/router.d.ts +2 -1
- package/dist/types/runtime.d.ts +11 -27
- package/dist/types/sandbox.d.ts +12 -0
- package/dist/types/scaffold.d.ts +0 -2
- package/dist/types/schema-query.d.ts +12 -0
- package/dist/types/server.d.ts +1 -4
- package/dist/types/signals.d.ts +25 -0
- package/dist/types/site.d.ts +0 -1
- package/dist/types/tooling.d.ts +115 -0
- package/dist/types/trusted-functions.d.ts +29 -0
- package/dist/types/types.d.ts +71 -7
- package/dist/types/typescript-authoring.d.ts +12 -0
- package/dist/types/vercel.d.ts +3 -1
- package/dist/types/verify-deployment.d.ts +47 -0
- package/dist/types.js +37 -5
- package/dist/typescript-authoring.js +142 -0
- package/dist/vercel.js +4 -3
- package/dist/verify-deployment.js +270 -0
- package/docs/AI-AUTHORING.md +232 -15
- package/docs/AWS.md +4 -4
- package/docs/BEST-PRACTICES.md +3 -2
- package/docs/BULK.md +79 -0
- package/docs/CAPABILITIES.md +192 -0
- package/docs/CAPACITY.md +129 -32
- package/docs/CI.md +142 -0
- package/docs/CLOUDFLARE.md +1 -2
- package/docs/COMPLIANCE.md +6 -9
- package/docs/CONDITIONS.md +74 -0
- package/docs/DEPLOYMENT-CHECKS.md +108 -0
- package/docs/EGRESS.md +125 -0
- package/docs/EXTENSIONS.md +398 -0
- package/docs/FRAMEWORK.md +198 -0
- package/docs/FUNCTION-SECURITY.md +129 -32
- package/docs/INSTALL.md +45 -12
- package/docs/INTERCHANGE.md +134 -0
- package/docs/LOAD-TESTING.md +4 -4
- package/docs/MIDDLEWARE-EXAMPLES.md +75 -0
- package/docs/MIDDLEWARE.md +29 -16
- package/docs/MONITORING.md +2 -19
- package/docs/NEXT-PHASE-PLAN.md +98 -0
- package/docs/NEXT-STEPS.md +634 -0
- package/docs/OBSERVABILITY.md +11 -18
- package/docs/OPEN-DECISIONS.md +212 -0
- package/docs/OPERATIONAL-PROOF.md +30 -31
- package/docs/OPERATIONS.md +29 -35
- package/docs/PLUGINS.md +37 -0
- package/docs/POLICIES.md +23 -309
- package/docs/PRERENDER.md +41 -1
- package/docs/PROJECT-DIRECTION.md +75 -8
- package/docs/PROVIDER-VERIFICATION.md +84 -0
- package/docs/READINESS.md +21 -1
- package/docs/README.md +87 -34
- package/docs/RECIPES.md +99 -0
- package/docs/RELEASE-READINESS.md +57 -35
- package/docs/RELEASE-SECURITY.md +116 -7
- 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 +41 -1
- package/docs/SPECIFICATION.md +150 -29
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +287 -0
- package/docs/SPIKE-BUSINESS-SUITE.md +1021 -0
- package/docs/SPIKE-CORE-LAYERING.md +337 -0
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +209 -0
- package/docs/SPIKE-EXTENSION-MODEL.md +419 -0
- package/docs/SPIKE-EXTENSIONS.md +6 -0
- package/docs/SPIKE-LAMBDA-COMPILE.md +201 -0
- package/docs/SPIKE-MONOREPO.md +322 -0
- package/docs/STANDARDS.md +150 -142
- package/docs/STARTERS.md +21 -1
- package/docs/STATIC.md +94 -0
- package/docs/TOOLING.md +295 -0
- package/docs/TUNNELS.md +0 -3
- package/docs/TYPESCRIPT-AUTHORING.md +82 -0
- package/docs/TYPESCRIPT.md +25 -4
- package/docs/USABILITY-REVIEW.md +129 -0
- package/docs/VERCEL.md +4 -5
- package/docs/VERSION-ALIGNMENT.md +205 -0
- package/docs/YAML-GUIDE.md +15 -479
- package/docs/YAML-REFERENCE.md +143 -22
- package/docs/policies/agents.md +1 -1
- package/docs/policies/cache.md +13 -0
- package/docs/policies/contract.md +52 -0
- package/docs/policies/hardened.md +56 -0
- package/docs/policies/interoperability.md +169 -0
- package/docs/policies/operations.md +45 -0
- package/docs/yaml/assets.md +36 -0
- package/docs/yaml/conditions.md +20 -0
- package/docs/yaml/functions.md +160 -0
- package/docs/yaml/middleware.md +29 -0
- package/docs/yaml/organization.md +74 -0
- package/docs/yaml/policies.md +37 -0
- package/docs/yaml/redirects.md +64 -0
- package/docs/yaml/responses.md +57 -0
- package/docs/yaml/site.md +24 -0
- package/examples/assets/example.yaml +17 -0
- package/examples/aws/example.yaml +20 -0
- package/examples/cloudflare/example.yaml +19 -0
- package/examples/compliance/example.yaml +11 -0
- package/examples/conditions/README.md +12 -0
- package/examples/conditions/example.yaml +19 -0
- package/examples/conditions/tests/requests.json +13 -0
- package/examples/conditions/urlcode.yaml +24 -0
- package/examples/cookbook/README.md +8 -4
- package/examples/cookbook/example.yaml +17 -0
- package/examples/cookbook/functions/catalog.mjs +3 -0
- package/examples/cookbook/functions/fail.mjs +4 -0
- package/examples/cookbook/functions/items.mjs +3 -0
- package/examples/cookbook/functions/profile.mjs +3 -0
- package/examples/cookbook/functions/resource.mjs +3 -0
- package/examples/cookbook/functions/status.mjs +3 -0
- package/examples/cookbook/middleware/auth.mjs +48 -0
- package/examples/cookbook/middleware/body.mjs +15 -0
- package/examples/cookbook/middleware/bucket.mjs +29 -0
- package/examples/cookbook/middleware/cors.mjs +21 -0
- package/examples/cookbook/middleware/debug.mjs +13 -0
- package/examples/cookbook/middleware/envelope.mjs +11 -0
- package/examples/cookbook/middleware/errors.mjs +11 -0
- package/examples/cookbook/middleware/etag.mjs +18 -0
- package/examples/cookbook/middleware/locale.mjs +20 -0
- package/examples/cookbook/middleware/maintenance.mjs +10 -0
- package/examples/cookbook/middleware/methods.mjs +15 -0
- package/examples/cookbook/middleware/negotiate.mjs +20 -0
- package/examples/cookbook/middleware/referer.mjs +12 -0
- package/examples/cookbook/middleware/request-id.mjs +16 -0
- package/examples/cookbook/route-index.json +676 -0
- package/examples/cookbook/routes/middleware.yaml +126 -0
- package/examples/cookbook/tests/requests.json +526 -0
- package/examples/cookbook/urlcode.yaml +1 -0
- package/examples/egress/README.md +22 -0
- package/examples/egress/example.yaml +19 -0
- package/examples/egress/urlcode.yaml +19 -0
- package/examples/extensions/README.md +7 -0
- package/examples/extensions/example.yaml +21 -0
- package/examples/extensions/urlcode.yaml +25 -0
- package/examples/monitoring/example.yaml +8 -0
- package/examples/prerender/README.md +2 -2
- package/examples/prerender/example.yaml +16 -0
- package/examples/provider-conformance/README.md +12 -0
- package/examples/provider-conformance/example.yaml +14 -0
- package/examples/provider-conformance/urlcode.yaml +34 -0
- package/examples/tunnel/example.yaml +8 -0
- package/examples/vercel/example.yaml +19 -0
- package/llms-full.txt +3084 -0
- package/llms.txt +61 -21
- package/package.json +36 -7
- package/packaging/claude-plugin/.claude-plugin/plugin.json +19 -0
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +122 -0
- package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +108 -0
- package/recipes/authenticated-json-api/README.md +51 -0
- package/recipes/authenticated-json-api/functions/profile.mjs +5 -0
- package/recipes/authenticated-json-api/recipe.yaml +34 -0
- package/recipes/authenticated-json-api/tests/requests.json +39 -0
- package/recipes/authenticated-json-api/urlcode.yaml +12 -0
- package/recipes/contact-form/README.md +25 -0
- package/recipes/contact-form/functions/contact.mjs +17 -0
- package/recipes/contact-form/recipe.yaml +33 -0
- package/recipes/contact-form/tests/requests.json +47 -0
- package/recipes/contact-form/urlcode.yaml +18 -0
- package/recipes/cors-api/README.md +16 -0
- package/recipes/cors-api/functions/items.mjs +3 -0
- package/recipes/cors-api/middleware/cors.mjs +21 -0
- package/recipes/cors-api/recipe.yaml +26 -0
- package/recipes/cors-api/tests/requests.json +65 -0
- package/recipes/cors-api/urlcode.yaml +12 -0
- package/recipes/health-page/README.md +13 -0
- package/recipes/health-page/recipe.yaml +23 -0
- package/recipes/health-page/tests/requests.json +36 -0
- package/recipes/health-page/urlcode.yaml +19 -0
- package/recipes/json-api/README.md +6 -0
- package/recipes/json-api/functions/echo.mjs +3 -0
- package/recipes/json-api/recipe.yaml +25 -0
- package/recipes/json-api/tests/requests.json +34 -0
- package/recipes/json-api/urlcode.yaml +12 -0
- package/recipes/middleware/README.md +34 -0
- package/recipes/middleware/functions/catalog.mjs +3 -0
- package/recipes/middleware/functions/fail.mjs +4 -0
- package/recipes/middleware/functions/items.mjs +3 -0
- package/recipes/middleware/functions/profile.mjs +3 -0
- package/recipes/middleware/functions/resource.mjs +3 -0
- package/recipes/middleware/functions/status.mjs +3 -0
- package/recipes/middleware/middleware/auth.mjs +48 -0
- package/recipes/middleware/middleware/body.mjs +15 -0
- package/recipes/middleware/middleware/bucket.mjs +29 -0
- package/recipes/middleware/middleware/cors.mjs +21 -0
- package/recipes/middleware/middleware/debug.mjs +13 -0
- package/recipes/middleware/middleware/envelope.mjs +11 -0
- package/recipes/middleware/middleware/errors.mjs +11 -0
- package/recipes/middleware/middleware/etag.mjs +18 -0
- package/recipes/middleware/middleware/locale.mjs +20 -0
- package/recipes/middleware/middleware/maintenance.mjs +10 -0
- package/recipes/middleware/middleware/methods.mjs +15 -0
- package/recipes/middleware/middleware/negotiate.mjs +20 -0
- package/recipes/middleware/middleware/referer.mjs +12 -0
- package/recipes/middleware/middleware/request-id.mjs +16 -0
- package/recipes/middleware/public/guide.txt +1 -0
- package/recipes/middleware/recipe.yaml +50 -0
- package/recipes/middleware/tests/requests.json +528 -0
- package/recipes/middleware/urlcode.yaml +127 -0
- package/recipes/protected-download/README.md +22 -0
- package/recipes/protected-download/files/report.txt +1 -0
- package/recipes/protected-download/recipe.yaml +31 -0
- package/recipes/protected-download/tests/requests.json +32 -0
- package/recipes/protected-download/urlcode.yaml +15 -0
- package/recipes/redirect/README.md +7 -0
- package/recipes/redirect/recipe.yaml +25 -0
- package/recipes/redirect/tests/requests.json +19 -0
- package/recipes/redirect/urlcode.yaml +9 -0
- package/recipes/static-plus-api/README.md +15 -0
- package/recipes/static-plus-api/functions/info.mjs +3 -0
- package/recipes/static-plus-api/public/assets/index.html +3 -0
- package/recipes/static-plus-api/public/assets/site.css +1 -0
- package/recipes/static-plus-api/public/index.html +8 -0
- package/recipes/static-plus-api/recipe.yaml +29 -0
- package/recipes/static-plus-api/tests/requests.json +56 -0
- package/recipes/static-plus-api/urlcode.yaml +23 -0
- package/recipes/typescript/README.md +8 -0
- package/recipes/typescript/functions/hello.ts +5 -0
- package/recipes/typescript/recipe.yaml +23 -0
- package/recipes/typescript/tests/requests.json +18 -0
- package/recipes/typescript/urlcode.yaml +5 -0
- package/recipes/webhook-receiver/README.md +20 -0
- package/recipes/webhook-receiver/functions/receive.mjs +16 -0
- package/recipes/webhook-receiver/recipe.yaml +27 -0
- package/recipes/webhook-receiver/tests/requests.json +59 -0
- package/recipes/webhook-receiver/urlcode.yaml +23 -0
- package/schemas/recipe.schema.json +139 -0
- package/schemas/urlcode.schema.json +659 -110
- package/skills/urlcode/SKILL.md +119 -0
- package/starters/default/.github/workflows/urlcode.yml +23 -0
- package/starters/default/.mcp.json +12 -0
- package/starters/default/AGENTS.md +79 -0
- 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 -41
- package/dist/sqlite-version.js +0 -6
- package/dist/types/link-api.d.ts +0 -30
- package/dist/types/link-cli.d.ts +0 -36
- 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 -9
- package/dist/types/sqlite-version.d.ts +0 -1
- package/docs/DYNAMIC-LINKS.md +0 -561
- package/docs/MANAGEMENT-SECURITY.md +0 -82
- package/examples/live-links/README.md +0 -11
- package/examples/live-links/tests/requests.json +0 -6
- package/examples/live-links/urlcode.yaml +0 -16
package/docs/OBSERVABILITY.md
CHANGED
|
@@ -33,15 +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
|
-
| `
|
|
37
|
-
| `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. |
|
|
38
|
-
| `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. |
|
|
36
|
+
| `signal` | `outcome` (`accepted`, `delivered`, `failed`, `dropped`), positive `count` | Best-effort webhook totals; no destination, request data or secrets. |
|
|
39
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. |
|
|
40
38
|
| `observer` | `status` `failed`, `name` string | An observer hook threw or rejected. Written to the default log only, never to observers. |
|
|
41
39
|
| `throttle` | `route`, `outcome` `allowed`/`exceeded`, `remaining` integer | A throttle decision. `allowed` is logged only in `mode: report`; enforce mode logs refusals. |
|
|
42
40
|
| `agents` | `route`, `list` string, `outcome` `denied`/`reported` | A User-Agent matched a list. The list name is logged, never the header. |
|
|
43
41
|
| `cache` | `route`, `outcome` `hit`/`stale`/`miss`/`store` | A cache lookup or store. |
|
|
44
|
-
| `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. |
|
|
45
42
|
| `listening` | `address`, `port`, `mode`, `origin` | Printed once by the CLI at startup, not emitted by the server. |
|
|
46
43
|
|
|
47
44
|
Every event carries `event` (its name). Numbers are JSON numbers, never
|
|
@@ -50,11 +47,10 @@ strings.
|
|
|
50
47
|
### Privacy guarantees
|
|
51
48
|
|
|
52
49
|
No event, snapshot or exposition carries a request URL, path, query string,
|
|
53
|
-
header, body, client address, User-Agent string, binding, secret
|
|
54
|
-
|
|
50
|
+
header, body, client address, User-Agent string, binding, secret or user
|
|
51
|
+
exception text. `route` is always a configured pattern
|
|
55
52
|
from reviewed YAML. `requestId` is server-generated unless
|
|
56
|
-
`--trust-request-id` accepts one from a trusted proxy.
|
|
57
|
-
`link_request` is redacted unless the operator sets `includeCode`. An observer
|
|
53
|
+
`--trust-request-id` accepts one from a trusted proxy. An observer
|
|
58
54
|
that logs should keep the same rule; nothing in an event lets it break it.
|
|
59
55
|
|
|
60
56
|
## Observers
|
|
@@ -97,12 +93,12 @@ call. A hook that throws or returns a rejecting promise is isolated: the
|
|
|
97
93
|
request is unaffected, the next observer still runs, `observers.errors` in
|
|
98
94
|
the snapshot increments and one `observer` record goes to the default log.
|
|
99
95
|
Nothing is retried; an observer that needs delivery guarantees owns its own
|
|
100
|
-
queue
|
|
96
|
+
queue.
|
|
101
97
|
|
|
102
98
|
`onMetrics` receives a fresh snapshot every `metricsIntervalMs`
|
|
103
99
|
(`startServer` option, `0` off by default, 1 s to 1 h) and once at `close()`.
|
|
104
|
-
`onClose` runs in reverse order after the runtime
|
|
105
|
-
|
|
100
|
+
`onClose` runs in reverse order after the runtime has closed.
|
|
101
|
+
`app.observers` lists the `{ name, version }` pairs.
|
|
106
102
|
|
|
107
103
|
`createRuntime(project, { observers })` takes the same array for embedding
|
|
108
104
|
without the server: the runtime's own sink and counters are then yours, and
|
|
@@ -123,7 +119,7 @@ and capped at 10 000 keys.
|
|
|
123
119
|
|
|
124
120
|
| Field | Type | Meaning |
|
|
125
121
|
|---|---|---|
|
|
126
|
-
| `version` | gauge | Snapshot shape version, currently `
|
|
122
|
+
| `version` | gauge | Snapshot shape version, currently `2`. |
|
|
127
123
|
| `uptimeSeconds`, `rssBytes` | gauge | Process facts. |
|
|
128
124
|
| `requests.total`, `requests.byStatusClass.{2xx,3xx,4xx,5xx}` | counter | Application responses, including shed 503s. |
|
|
129
125
|
| `requests.inFlight` | gauge | Requests holding application admission now. |
|
|
@@ -134,12 +130,10 @@ and capped at 10 000 keys.
|
|
|
134
130
|
| `watch.failed` | counter | Development watcher failures. |
|
|
135
131
|
| `functionWorkers.started`, `functionWorkers.restarts` | counter | Worker starts and scheduled replacements. |
|
|
136
132
|
| `functionWorkers.healthySlots`, `functionWorkers.slots` | gauge | Ready slots and configured slots of the serving runtime. |
|
|
137
|
-
| `linkStoreWorkers.started`, `linkStoreWorkers.restarts` | counter | The same for link-store connections. |
|
|
138
133
|
| `policies.throttle.{allowed,exceeded}` | counter | Throttle decisions (see the catalogue for what enforce mode logs). |
|
|
139
134
|
| `policies.agents.{denied,reported}` | counter | Agents decisions. |
|
|
140
135
|
| `policies.cache.{hit,stale,miss,store}` | counter | Cache outcomes. |
|
|
141
|
-
| `
|
|
142
|
-
| `linkObserver.failed`, `linkObserver.dropped` | counter | Link collector failures and dropped events. |
|
|
136
|
+
| `signals.{accepted,delivered,failed,dropped}` | counter | Best-effort webhook outcomes; exposed as `signals_total` with outcome labels. |
|
|
143
137
|
| `logsDropped` | counter | Records the JSON logger shed. |
|
|
144
138
|
| `observers.errors` | counter | Observer hooks that threw or rejected. |
|
|
145
139
|
|
|
@@ -151,8 +145,8 @@ aggregation across replicas is the scraper's job.
|
|
|
151
145
|
|
|
152
146
|
## Prometheus exposition
|
|
153
147
|
|
|
154
|
-
`startServer({ metrics: true })
|
|
155
|
-
`text/plain; version=0.0.4`, rendered from the same snapshot by
|
|
148
|
+
`startServer({ metrics: true })`, or `urlcode serve --metrics` on the command
|
|
149
|
+
line, serves `GET /_urlcode/metrics` as `text/plain; version=0.0.4`, rendered from the same snapshot by
|
|
156
150
|
`renderPrometheus(snapshot)`, a pure function you can also call yourself.
|
|
157
151
|
Every metric is prefixed `urlcode_`; counters end in `_total`; the only labels
|
|
158
152
|
are `status_class`, `route` and `outcome`.
|
|
@@ -169,7 +163,6 @@ urlcode_function_worker_restarts_total 0
|
|
|
169
163
|
urlcode_function_worker_healthy_slots 2
|
|
170
164
|
urlcode_throttle_total{outcome="exceeded"} 14
|
|
171
165
|
urlcode_cache_total{outcome="hit"} 511
|
|
172
|
-
urlcode_link_requests_total{outcome="completed"} 0
|
|
173
166
|
urlcode_logs_dropped_total 0
|
|
174
167
|
urlcode_observer_errors_total 0
|
|
175
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,42 +1,41 @@
|
|
|
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
|
|
|
23
|
-
Local/CI passes do not close these gates.
|
|
22
|
+
Local/CI passes do not close these gates. `urlcode verify-deployment --target`
|
|
23
|
+
([deployment checks](DEPLOYMENT-CHECKS.md)) records that the deployed responses
|
|
24
|
+
match the project and is the first step of the rollback drill below, not a
|
|
25
|
+
substitute for it. The deployment owner must record:
|
|
24
26
|
|
|
25
27
|
| Exercise | Acceptance evidence |
|
|
26
28
|
|---|---|
|
|
27
|
-
| Soak | At least 24 hours at expected peak and burst load through actual TLS/ingress; native/function
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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.
|
|
34
37
|
|
|
35
38
|
Record date, operator, source/app/policy/image digests, topology, hardware/limits,
|
|
36
|
-
commands, duration, synthetic dataset size, raw metrics/log locations, result
|
|
37
|
-
|
|
38
|
-
record is not a pass.
|
|
39
|
-
The executable local drill uses a fully quiesced, explicitly checkpointed database intentionally. A last
|
|
40
|
-
read-only connection can leave WAL frames even after every connection closes;
|
|
41
|
-
closing alone is insufficient proof that the main database file is a full backup. For a
|
|
42
|
-
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
|
@@ -35,18 +35,20 @@ The supplied image packages the runtime; it does not copy your application or
|
|
|
35
35
|
local secret files. Build from the reviewed runtime checkout:
|
|
36
36
|
|
|
37
37
|
```sh
|
|
38
|
-
docker build -t urlcode:0.
|
|
38
|
+
docker build -t urlcode:0.3.0 .
|
|
39
39
|
docker run --rm --name my-links \
|
|
40
40
|
--read-only --cap-drop ALL --security-opt no-new-privileges \
|
|
41
41
|
--memory 512m --cpus 1 --pids-limit 128 \
|
|
42
42
|
-p 127.0.0.1:3000:3000 \
|
|
43
43
|
-v "$PWD/starters/default:/project:ro" \
|
|
44
|
-
urlcode:0.
|
|
44
|
+
urlcode:0.3.0
|
|
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,30 +149,23 @@ 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.
|
|
164
163
|
2. Validate its config/bindings and run local HTTP tests without external redirects.
|
|
165
164
|
3. Start it on an alternate private port/container. Check readiness and representative
|
|
166
|
-
redirect/function behavior through the intended proxy configuration
|
|
165
|
+
redirect/function behavior through the intended proxy configuration:
|
|
166
|
+
`urlcode verify-deployment --project . --target https://candidate.host` compares
|
|
167
|
+
version, fixtures, policy headers and site files with the project
|
|
168
|
+
([deployment checks](DEPLOYMENT-CHECKS.md)).
|
|
167
169
|
4. Switch proxy traffic after checks pass. Drain the previous instance before stopping.
|
|
168
170
|
5. If checks or observed behavior fail, route traffic back to the retained previous
|
|
169
171
|
instance/image and its compatible secret bindings.
|
|
@@ -171,7 +173,7 @@ this file over a network filesystem; no distributed adapter is included yet.
|
|
|
171
173
|
This is an operator procedure, not an implemented deployment control plane.
|
|
172
174
|
Rollback cannot undo a function's external side effects or migrate an app's
|
|
173
175
|
state automatically. Plan those independently. Keep Git definitions backed up;
|
|
174
|
-
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.
|
|
175
177
|
|
|
176
178
|
## Capacity and incident planning
|
|
177
179
|
|
|
@@ -197,11 +199,3 @@ adapter is made by the current release.
|
|
|
197
199
|
The [2026-09-16 internal audit](SECURITY-AUDIT.md) records fixes, regression evidence
|
|
198
200
|
and remaining security/operational gates. This is not an independent assessment.
|
|
199
201
|
|
|
200
|
-
## Management hardening baseline
|
|
201
|
-
|
|
202
|
-
Management is now restricted to literal loopback addresses. Prefer `--auth-file`
|
|
203
|
-
for individual expiring, revocable credentials with collection/action scopes.
|
|
204
|
-
Every successful built-in store mutation has an atomic, durable SQLite audit row;
|
|
205
|
-
HTTP request logs remain best effort. See [management security](MANAGEMENT-SECURITY.md)
|
|
206
|
-
for policy examples, compatibility, archival and rollback requirements, and
|
|
207
|
-
[operational proof](OPERATIONAL-PROOF.md) for executable recovery drills.
|
package/docs/PLUGINS.md
CHANGED
|
@@ -42,6 +42,7 @@ const auditPlugin = {
|
|
|
42
42
|
name: 'audit', // ^[a-z][a-z0-9-]{0,63}$, unique per runtime
|
|
43
43
|
version: '1.0.0', // any string up to 64 characters
|
|
44
44
|
targets: ['node', 'vercel'], // subset of node, vercel, aws, cloudflare
|
|
45
|
+
credentialHeaders: ['Cookie', 'Authorization'], // optional; withheld from guests
|
|
45
46
|
async onActivate(runtime) {}, // may throw to refuse activation
|
|
46
47
|
async onRequest(request) {}, // return a result to short-circuit
|
|
47
48
|
async onResponse(request, result) { return result; }, // return the result to send
|
|
@@ -112,6 +113,42 @@ it reaches the guest: there is no sandbox handle, no deadline, no `env` or
|
|
|
112
113
|
deadline, read or write guest state, or obtain a binding the operator policy
|
|
113
114
|
did not grant to the route.
|
|
114
115
|
|
|
116
|
+
## Withholding credential headers from application code
|
|
117
|
+
|
|
118
|
+
An operator plugin may declare `credentialHeaders: ['Cookie', 'Authorization']`.
|
|
119
|
+
Each list contains at most 64 unique HTTP header names, compared
|
|
120
|
+
case-insensitively, each at most 128 characters. The runtime validates the lists
|
|
121
|
+
and captures their union before activation hooks run. Later mutation of a plugin
|
|
122
|
+
object does not weaken the running boundary; a reload captures a new declaration.
|
|
123
|
+
No YAML key can enable, disable or override this operator setting.
|
|
124
|
+
|
|
125
|
+
The union applies to every matched route in that runtime, including public
|
|
126
|
+
functions and guest middleware on native routes. Before processing application
|
|
127
|
+
inputs, the runtime makes a separate header copy and removes those names. Guest
|
|
128
|
+
`Request.headers`, `ctx.inputs.header` and header-derived `ctx.args` receive no
|
|
129
|
+
corresponding values. Header schema defaults are also omitted from the guest
|
|
130
|
+
context for those names. A required protected header input without a default fails with
|
|
131
|
+
`Missing required parameter`, even when the original request supplied it;
|
|
132
|
+
projects should not declare credential inputs that the operator withholds.
|
|
133
|
+
Native redirect query mappings also use this sanitized application context, so
|
|
134
|
+
they cannot reflect a withheld header into a response visible to middleware.
|
|
135
|
+
Selecting `Cookie` removes the entire header, not just one named cookie.
|
|
136
|
+
|
|
137
|
+
Host request/response/error hooks, first-party policies and request-body checks
|
|
138
|
+
retain the original headers. The runtime does not mutate the caller's Headers
|
|
139
|
+
object. Existing applications behave exactly as before when no plugin declares
|
|
140
|
+
credential headers. Plugins still need at least one lifecycle or request hook;
|
|
141
|
+
a header list alone is not a complete plugin.
|
|
142
|
+
|
|
143
|
+
This is a data boundary, not authentication, authorization or cache protection.
|
|
144
|
+
It does not create a principal, validate a token, protect a route, or prevent
|
|
145
|
+
shared caching. In particular, withholding Cookie or Authorization from guests
|
|
146
|
+
does not make a personalized response safe to cache. Operators must separately
|
|
147
|
+
configure the appropriate authentication and cache behavior. It also does not
|
|
148
|
+
redact URL/query/body content, explicit binding grants, logs or values deliberately
|
|
149
|
+
reflected by trusted host plugins. Host plugins retain full access to the request
|
|
150
|
+
and remain trusted operator code.
|
|
151
|
+
|
|
115
152
|
## Results
|
|
116
153
|
|
|
117
154
|
A result is what the runtime writes:
|