@nylorun/runtime 0.26.0-beta → 0.28.0-beta
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/CHANGELOG.md +171 -0
- package/README.md +18 -14
- package/dist/adapters/execution/restate.d.ts +15 -18
- package/dist/adapters/execution/restate.js +31 -27
- package/dist/adapters/replication/pgoutput.d.ts +1 -1
- package/dist/adapters/replication/pgoutput.js +3 -2
- package/dist/adapters/sandbox/local.js +8 -5
- package/dist/adapters/sandbox/virtual.js +2 -2
- package/dist/adapters/streams/s2.d.ts +1 -1
- package/dist/adapters/streams/s2.js +18 -14
- package/dist/api/a2a/card.d.ts +4 -3
- package/dist/api/a2a/card.js +4 -4
- package/dist/api/a2a/endpoint.js +3 -4
- package/dist/api/a2a/protocol.js +4 -0
- package/dist/api/a2a/routes.d.ts +2 -6
- package/dist/api/a2a/routes.js +30 -25
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +8 -11
- package/dist/api/components.d.ts +6789 -3405
- package/dist/api/components.js +43 -30
- package/dist/api/gate/keys.js +7 -4
- package/dist/api/gate/routes.d.ts +6 -2
- package/dist/api/gate/routes.js +15 -11
- package/dist/api/http/app.js +9 -4
- package/dist/api/http/body.d.ts +17 -3
- package/dist/api/http/body.js +35 -7
- package/dist/api/http/respond.js +6 -2
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/agent-manifests.d.ts +10 -0
- package/dist/api/http/routes/agent-manifests.js +135 -0
- package/dist/api/http/routes/agents.d.ts +11 -0
- package/dist/api/http/routes/agents.js +418 -0
- package/dist/api/http/routes/artifacts.js +21 -9
- package/dist/api/http/routes/coverage.d.ts +4 -4
- package/dist/api/http/routes/coverage.js +13 -7
- package/dist/api/http/routes/files.d.ts +2 -1
- package/dist/api/http/routes/files.js +31 -5
- package/dist/api/http/routes/mcp.js +3 -3
- package/dist/api/http/routes/me.d.ts +1 -1
- package/dist/api/http/routes/me.js +1 -2
- package/dist/api/http/routes/memories.d.ts +9 -0
- package/dist/api/http/routes/memories.js +152 -0
- package/dist/api/http/routes/reads.d.ts +10 -111
- package/dist/api/http/routes/reads.js +51 -29
- package/dist/api/http/routes/sessions.js +26 -54
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +77 -24
- package/dist/api/http/routes/triggers.d.ts +10 -0
- package/dist/api/http/routes/triggers.js +180 -0
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +73 -47
- package/dist/artifacts/disk.d.ts +10 -14
- package/dist/artifacts/disk.js +99 -95
- package/dist/artifacts/files.d.ts +13 -4
- package/dist/artifacts/files.js +27 -9
- package/dist/artifacts/folder-upload.d.ts +75 -0
- package/dist/artifacts/folder-upload.js +263 -0
- package/dist/artifacts/folders.d.ts +14 -8
- package/dist/artifacts/folders.js +19 -11
- package/dist/artifacts/links.js +7 -6
- package/dist/artifacts/parts.d.ts +9 -14
- package/dist/artifacts/parts.js +11 -9
- package/dist/artifacts/pipe.d.ts +15 -0
- package/dist/artifacts/pipe.js +71 -0
- package/dist/artifacts/quota.d.ts +36 -0
- package/dist/artifacts/quota.js +44 -0
- package/dist/artifacts/service.d.ts +25 -1
- package/dist/artifacts/service.js +72 -27
- package/dist/blob/fs.js +2 -1
- package/dist/blob/s3.d.ts +6 -2
- package/dist/blob/s3.js +9 -7
- package/dist/blob/types.d.ts +1 -1
- package/dist/blob/types.js +1 -1
- package/dist/core/flow-host.d.ts +0 -1
- package/dist/core/limits.d.ts +11 -35
- package/dist/core/limits.js +5 -73
- package/dist/core/sandbox-routes.js +0 -1
- package/dist/core/turn-manifest.d.ts +1 -2
- package/dist/core/turn-manifest.js +4 -28
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +8 -6
- package/dist/execution/types.d.ts +1 -6
- package/dist/execution/types.js +0 -5
- package/dist/gates/contract.d.ts +3 -2
- package/dist/gates/contract.js +1 -1
- package/dist/gates/egress-credentials.js +5 -4
- package/dist/gates/egress.d.ts +13 -13
- package/dist/gates/egress.js +19 -21
- package/dist/gates/fixture-model.js +13 -2
- package/dist/gates/handler.d.ts +1 -3
- package/dist/gates/handler.js +4 -3
- package/dist/gates/http-client.d.ts +4 -3
- package/dist/gates/http-client.js +5 -7
- package/dist/gates/http-tool.d.ts +24 -4
- package/dist/gates/http-tool.js +41 -13
- package/dist/gates/in-process.d.ts +2 -1
- package/dist/gates/in-process.js +17 -9
- package/dist/gates/inflight.d.ts +2 -2
- package/dist/gates/inflight.js +3 -2
- package/dist/gates/journal.js +5 -7
- package/dist/gates/mcp-handler.d.ts +4 -3
- package/dist/gates/mcp-handler.js +15 -5
- package/dist/gates/meter.js +6 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +34 -6
- package/dist/gates/policy.js +66 -18
- package/dist/gates/provider-gate.d.ts +22 -0
- package/dist/gates/provider-gate.js +30 -0
- package/dist/gates/tool-calls.d.ts +1 -3
- package/dist/gates/tool-calls.js +2 -3
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +5 -6
- package/dist/gates/tool-contract.d.ts +8 -5
- package/dist/gates/tool-contract.js +1 -1
- package/dist/gates/tool-gate.d.ts +18 -7
- package/dist/gates/tool-gate.js +49 -7
- package/dist/harness/calls.d.ts +27 -25
- package/dist/harness/calls.js +40 -46
- package/dist/harness/executors.d.ts +10 -3
- package/dist/harness/executors.js +53 -23
- package/dist/harness/main.js +2 -2
- package/dist/harness/pod.d.ts +2 -2
- package/dist/harness/pod.js +7 -6
- package/dist/harness/run-tokens.d.ts +4 -1
- package/dist/harness/run-tokens.js +6 -2
- package/dist/harness/service.d.ts +17 -14
- package/dist/harness/service.js +21 -35
- package/dist/harness/setup.d.ts +0 -2
- package/dist/harness/setup.js +21 -15
- package/dist/harness/ws-client.d.ts +2 -2
- package/dist/harness/ws-client.js +7 -5
- package/dist/harness/ws-port.d.ts +3 -4
- package/dist/harness/ws-port.js +3 -4
- package/dist/harness-api/artifacts.d.ts +20 -0
- package/dist/harness-api/artifacts.js +478 -0
- package/dist/harness-api/in-process.d.ts +2 -2
- package/dist/harness-api/in-process.js +18 -16
- package/dist/harness-api/record.d.ts +10 -9
- package/dist/harness-api/record.js +113 -157
- package/dist/harness-api/renew.d.ts +1 -1
- package/dist/harness-api/renew.js +2 -2
- package/dist/harness-api/server.d.ts +1 -16
- package/dist/harness-api/server.js +86 -73
- package/dist/harness-api/start.d.ts +27 -5
- package/dist/harness-api/start.js +102 -19
- package/dist/harness-api/tool-journal.d.ts +15 -10
- package/dist/harness-api/tool-journal.js +26 -24
- package/dist/harness-api/workspace.d.ts +0 -4
- package/dist/harness-api/workspace.js +4 -47
- package/dist/harness-api/ws-server.js +10 -10
- package/dist/host/create-host.js +2 -3
- package/dist/host/execution.js +2 -3
- package/dist/host/gates.d.ts +2 -9
- package/dist/host/gates.js +15 -12
- package/dist/host/http.d.ts +2 -4
- package/dist/host/http.js +5 -29
- package/dist/host/main.js +10 -3
- package/dist/host/operate.js +12 -3
- package/dist/host/stack-config.d.ts +11 -0
- package/dist/host/stack-config.js +21 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/index.js +2 -1
- package/dist/infra/readiness.d.ts +0 -2
- package/dist/infra/readiness.js +4 -3
- package/dist/keys/client.d.ts +1 -0
- package/dist/keys/client.js +2 -3
- package/dist/keys/contract.d.ts +0 -2
- package/dist/keys/contract.js +0 -2
- package/dist/keys/egress-ca.d.ts +0 -2
- package/dist/keys/egress-ca.js +2 -3
- package/dist/keys/keys.d.ts +15 -4
- package/dist/keys/keys.js +22 -1
- package/dist/keys/webhooks.d.ts +25 -0
- package/dist/keys/webhooks.js +62 -0
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +3043 -1954
- package/dist/mcp/connect.d.ts +35 -26
- package/dist/mcp/connect.js +112 -141
- package/dist/mcp/pool.d.ts +27 -22
- package/dist/mcp/pool.js +34 -48
- package/dist/mcp/preview.d.ts +12 -5
- package/dist/mcp/preview.js +43 -26
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/memory/service.d.ts +97 -0
- package/dist/memory/service.js +335 -0
- package/dist/memory/tool.d.ts +36 -0
- package/dist/memory/tool.js +84 -0
- package/dist/model/auth-store.js +5 -3
- package/dist/model/capabilities.d.ts +1 -1
- package/dist/model/catalog.d.ts +9 -36
- package/dist/model/catalog.js +6 -33
- package/dist/model/descriptor.d.ts +51 -0
- package/dist/model/descriptor.js +83 -0
- package/dist/model/models.d.ts +0 -3
- package/dist/model/models.js +3 -5
- package/dist/model/native-blocks.js +1 -1
- package/dist/model/pi-model.d.ts +16 -5
- package/dist/model/pi-model.js +86 -105
- package/dist/openapi.json +13653 -9986
- package/dist/reads/cursor.js +3 -1
- package/dist/reads/types.d.ts +3 -3
- package/dist/record/agent.d.ts +27 -0
- package/dist/record/agent.js +46 -0
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/index.d.ts +2 -2
- package/dist/record/index.js +2 -2
- package/dist/redact.d.ts +1 -1
- package/dist/redact.js +3 -2
- package/dist/sandbox/egress-token.d.ts +3 -3
- package/dist/sandbox/egress-token.js +3 -3
- package/dist/sandbox/join.js +8 -7
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +2 -2
- package/dist/sandbox/placement.js +7 -10
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +25 -17
- package/dist/sandbox/pods/lifecycle.js +51 -29
- package/dist/sandbox/pods/network-gate.d.ts +4 -5
- package/dist/sandbox/pods/network-gate.js +7 -7
- package/dist/sandbox/pods/reconcile.js +14 -3
- package/dist/sandbox/pods/spec.js +8 -5
- package/dist/sandbox/policy.d.ts +7 -1
- package/dist/sandbox/policy.js +10 -3
- package/dist/sandbox/resolve.d.ts +11 -6
- package/dist/sandbox/resolve.js +19 -9
- package/dist/sandbox/setup.d.ts +6 -2
- package/dist/sandbox/setup.js +47 -14
- package/dist/sandbox/share.d.ts +5 -14
- package/dist/sandbox/share.js +5 -34
- package/dist/sandbox/skills.d.ts +7 -13
- package/dist/sandbox/skills.js +16 -31
- package/dist/sandbox/tenant-config.d.ts +2 -2
- package/dist/sandbox/tenant-config.js +4 -3
- package/dist/sandbox/tools.d.ts +7 -1
- package/dist/sandbox/tools.js +26 -26
- package/dist/sandbox/types.d.ts +10 -3
- package/dist/sandbox/types.js +9 -0
- package/dist/store/postgres/connect.d.ts +6 -5
- package/dist/store/postgres/connect.js +14 -11
- package/dist/store/postgres/control.js +13 -13
- package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
- package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
- package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
- package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
- package/dist/store/postgres/drizzle/0032_agents.sql +54 -0
- package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
- package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
- package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +63 -0
- package/dist/store/postgres/reads.js +17 -82
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/schema.d.ts +1382 -151
- package/dist/store/postgres/schema.js +191 -37
- package/dist/store/postgres/store.js +384 -105
- package/dist/store/postgres/tenant.js +1 -2
- package/dist/store/types.d.ts +172 -57
- package/dist/store/types.js +0 -1
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +41 -39
- package/dist/tenant/agent-manifests.d.ts +72 -0
- package/dist/tenant/agent-manifests.js +359 -0
- package/dist/tenant/agent-versions.d.ts +126 -0
- package/dist/tenant/agent-versions.js +389 -0
- package/dist/tenant/agents.d.ts +230 -0
- package/dist/tenant/{environments.js → agents.js} +493 -328
- package/dist/tenant/auth.d.ts +1 -1
- package/dist/tenant/auth.js +3 -3
- package/dist/tenant/commands.d.ts +63 -2
- package/dist/tenant/commands.js +240 -90
- package/dist/tenant/context.d.ts +57 -33
- package/dist/tenant/context.js +6 -2
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +15 -51
- package/dist/tenant/ephemeral.js +2 -1
- package/dist/tenant/history.d.ts +1 -1
- package/dist/tenant/history.js +1 -1
- package/dist/tenant/host-token.d.ts +3 -3
- package/dist/tenant/host-token.js +11 -10
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +29 -6
- package/dist/tenant/identity-file.js +45 -15
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +34 -26
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +87 -0
- package/dist/tenant/limits.js +172 -0
- package/dist/tenant/memories.d.ts +38 -0
- package/dist/tenant/memories.js +119 -0
- package/dist/tenant/module.d.ts +4 -1
- package/dist/tenant/module.js +9 -8
- package/dist/tenant/outbound.d.ts +11 -14
- package/dist/tenant/outbound.js +10 -15
- package/dist/tenant/reset.js +2 -2
- package/dist/tenant/run-grants.d.ts +6 -0
- package/dist/tenant/run-grants.js +7 -2
- package/dist/tenant/run-token.d.ts +0 -4
- package/dist/tenant/run-token.js +6 -11
- package/dist/tenant/runtime-limits.d.ts +24 -0
- package/dist/tenant/runtime-limits.js +27 -0
- package/dist/tenant/runtime.d.ts +1 -1
- package/dist/tenant/runtime.js +38 -43
- package/dist/tenant/session-streams.js +7 -6
- package/dist/tenant/session.d.ts +1 -1
- package/dist/tenant/session.js +1 -1
- package/dist/tenant/sessions.d.ts +28 -45
- package/dist/tenant/sessions.js +89 -209
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +10 -10
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +51 -51
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/triggers.d.ts +143 -0
- package/dist/tenant/triggers.js +715 -0
- package/dist/tenant/turn-timing.d.ts +11 -1
- package/dist/tenant/turn-timing.js +3 -1
- package/dist/tenant/types.d.ts +9 -14
- package/dist/tenant/worker.d.ts +4 -10
- package/dist/tenant/worker.js +10 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +10 -7
- package/dist/traces/map.js +16 -12
- package/dist/traces/otlp.d.ts +3 -3
- package/dist/traces/otlp.js +5 -3
- package/dist/traces/projector.d.ts +9 -4
- package/dist/traces/projector.js +13 -7
- package/dist/traces/read.js +6 -1
- package/dist/tuning.d.ts +390 -0
- package/dist/tuning.js +677 -0
- package/dist/vault/headers.js +4 -2
- package/dist/vault/service.d.ts +13 -8
- package/dist/vault/service.js +61 -17
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +13 -12
- package/dist/api/http/routes/environment-manifests.d.ts +0 -9
- package/dist/api/http/routes/environment-manifests.js +0 -118
- package/dist/api/http/routes/environments.d.ts +0 -11
- package/dist/api/http/routes/environments.js +0 -332
- package/dist/artifacts/export.d.ts +0 -8
- package/dist/artifacts/export.js +0 -204
- package/dist/artifacts/media-types.d.ts +0 -30
- package/dist/artifacts/media-types.js +0 -137
- package/dist/artifacts/workspace.d.ts +0 -60
- package/dist/artifacts/workspace.js +0 -45
- package/dist/media.d.ts +0 -10
- package/dist/media.js +0 -58
- package/dist/record/environment.d.ts +0 -25
- package/dist/record/environment.js +0 -44
- package/dist/sandbox/runtime-tools.d.ts +0 -9
- package/dist/sandbox/runtime-tools.js +0 -16
- package/dist/sandbox/session-sandbox.d.ts +0 -16
- package/dist/sandbox/session-sandbox.js +0 -77
- package/dist/tenant/artifact-tool.d.ts +0 -13
- package/dist/tenant/artifact-tool.js +0 -243
- package/dist/tenant/environment-manifests.d.ts +0 -57
- package/dist/tenant/environment-manifests.js +0 -200
- package/dist/tenant/environments.d.ts +0 -174
- package/dist/tenant/skill-tool.d.ts +0 -20
- package/dist/tenant/skill-tool.js +0 -67
- package/dist/tenant/tool-results.d.ts +0 -57
- package/dist/tenant/tool-results.js +0 -298
- package/dist/tenant/tool-search.d.ts +0 -39
- package/dist/tenant/tool-search.js +0 -103
|
@@ -41,11 +41,14 @@
|
|
|
41
41
|
* `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, with `OTEL_EXPORTER_OTLP_HEADERS`), what they say of
|
|
42
42
|
* their source (`OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`) and which content they carry
|
|
43
43
|
* (`NYLORUN_TRACE_CONTENT`, or all of it with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`).
|
|
44
|
+
* So is the Host tuning (`NYLORUN_TUNING`, `tuning.ts`), for every service: its timings,
|
|
45
|
+
* retries and internal sizes.
|
|
44
46
|
*/
|
|
45
47
|
import { DEFAULT_SERVICE_NAME } from "../traces/otlp.js";
|
|
46
48
|
import { TRACE_CONTENT_KINDS } from "../traces/map.js";
|
|
47
49
|
import { isTenantId } from "@nylorun/core/compatibility";
|
|
48
50
|
import { blockedAddresses } from "../sandbox/pods/network-gate.js";
|
|
51
|
+
import { HostTuningError, parseHostTuning } from "../tuning.js";
|
|
49
52
|
export const RUNTIME_SERVICES = ["core", "loop", "gates", "keys", "egress", "harness"];
|
|
50
53
|
/**
|
|
51
54
|
* Services that may share a process (D12): they hold the same secrets and parse the same
|
|
@@ -254,8 +257,9 @@ function parseIdentityKeys(env) {
|
|
|
254
257
|
/** Parse the stack configuration; throws `StackConfigError` naming the variable. */
|
|
255
258
|
export function parseStackConfig(env, argv) {
|
|
256
259
|
const { services, deprecatedRole } = parseServices(argv);
|
|
260
|
+
const tuning = parseTuning(env);
|
|
257
261
|
if (services.has("harness"))
|
|
258
|
-
return { services, harness: parseHarnessService(env), endpoints: {} };
|
|
262
|
+
return { services, harness: parseHarnessService(env), endpoints: {}, ...(tuning ? { tuning } : {}) };
|
|
259
263
|
// The image sets NYLORUN_LISTEN_*: only the API's processes read them.
|
|
260
264
|
const servesApi = services.has("core") || services.has("loop");
|
|
261
265
|
const listen = servesApi ? parseListen(env) : undefined;
|
|
@@ -345,8 +349,24 @@ export function parseStackConfig(env, argv) {
|
|
|
345
349
|
...(tenant ? { tenant } : {}),
|
|
346
350
|
...(objectStore ? { objectStore } : {}),
|
|
347
351
|
...(traces ? { traces } : {}),
|
|
352
|
+
...(tuning ? { tuning } : {}),
|
|
348
353
|
};
|
|
349
354
|
}
|
|
355
|
+
/** `StackConfig.tuning` from `NYLORUN_TUNING`, or undefined when it is unset. */
|
|
356
|
+
function parseTuning(env) {
|
|
357
|
+
const raw = read(env, "NYLORUN_TUNING");
|
|
358
|
+
if (raw === undefined)
|
|
359
|
+
return undefined;
|
|
360
|
+
try {
|
|
361
|
+
const { tuning, overrides } = parseHostTuning(raw);
|
|
362
|
+
return { values: tuning, overrides: overrides ?? {} };
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
if (error instanceof HostTuningError)
|
|
366
|
+
throw new StackConfigError(error.message);
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
350
370
|
/**
|
|
351
371
|
* `StackConfig.traces` (R7.2). The OTLP endpoint is the collector's base URL, to which
|
|
352
372
|
* `/v1/traces` is added; `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` is the full URL and wins. Headers
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ export { createTrustedIssuers, type TrustedIssuers, type TrustedIssuersOptions,
|
|
|
5
5
|
export type { TenantConfig, TenantHandle, TenantPaths, Logger, OpenTenantRuntime, } from "./tenant/types.js";
|
|
6
6
|
export { scriptedModel, gatewayModel, type ModelProvider } from "./core/provider.js";
|
|
7
7
|
export type { RuntimeAgent } from "./contracts.js";
|
|
8
|
-
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
|
|
9
8
|
export type { RuntimeModelAdapter } from "./contracts.js";
|
|
10
9
|
export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
|
|
11
10
|
export type { HostProcessPathInputs } from "./host/environment.js";
|
package/dist/index.js
CHANGED
|
@@ -3,5 +3,4 @@ export { startEphemeralRuntime, } from "./tenant/ephemeral.js";
|
|
|
3
3
|
export { parseIdentityFile, IdentityFileError, } from "./tenant/identity-file.js";
|
|
4
4
|
export { createTrustedIssuers, } from "./tenant/issuers.js";
|
|
5
5
|
export { scriptedModel, gatewayModel } from "./core/provider.js";
|
|
6
|
-
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
|
|
7
6
|
export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
|
package/dist/infra/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createDatabase } from "./database.js";
|
|
|
2
2
|
import { createExecution, executionKind } from "./execution.js";
|
|
3
3
|
import { createReadiness, infraProbes } from "./readiness.js";
|
|
4
4
|
import { createStreams, streamsKind } from "./streams.js";
|
|
5
|
+
import { hostTuning } from "../tuning.js";
|
|
5
6
|
export { createDatabase, probeDatabase } from "./database.js";
|
|
6
7
|
export { createExecution, probeExecution, WORKER_LISTEN } from "./execution.js";
|
|
7
8
|
export { createReadiness, infraProbes } from "./readiness.js";
|
|
@@ -30,7 +31,7 @@ export function createInfra(config, options = {}) {
|
|
|
30
31
|
...(streams ? { streams } : {}),
|
|
31
32
|
...(readiness ? { readiness } : {}),
|
|
32
33
|
async close() {
|
|
33
|
-
await Promise.allSettled([database?.end({ timeout:
|
|
34
|
+
await Promise.allSettled([database?.end({ timeout: hostTuning().postgres.closeTimeoutSeconds }), streams?.close()]);
|
|
34
35
|
},
|
|
35
36
|
};
|
|
36
37
|
}
|
|
@@ -21,8 +21,6 @@ export interface ReadinessReport {
|
|
|
21
21
|
errors: Record<string, string>;
|
|
22
22
|
}
|
|
23
23
|
export type Readiness = () => Promise<ReadinessReport>;
|
|
24
|
-
/** Default bound on one readiness pass. Compose probes `/ready` every 2 s with a 5 s timeout. */
|
|
25
|
-
export declare const READINESS_TIMEOUT_MS = 2000;
|
|
26
24
|
export declare function createReadiness(probes: Readonly<Record<string, Probe>>, options?: {
|
|
27
25
|
timeoutMs?: number;
|
|
28
26
|
}): Readiness;
|
package/dist/infra/readiness.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { probeDatabase } from "./database.js";
|
|
2
2
|
import { probeExecution } from "./execution.js";
|
|
3
|
-
|
|
4
|
-
export const READINESS_TIMEOUT_MS = 2000;
|
|
3
|
+
import { hostTuning } from "../tuning.js";
|
|
5
4
|
export function createReadiness(probes, options = {}) {
|
|
6
|
-
|
|
5
|
+
// The bound on one pass: `host.readinessTimeoutMs` (Host tuning). Compose probes `/ready` every
|
|
6
|
+
// 2 s with a 5 s timeout.
|
|
7
|
+
const timeoutMs = options.timeoutMs ?? hostTuning().host.readinessTimeoutMs;
|
|
7
8
|
return async () => {
|
|
8
9
|
const entries = Object.entries(probes);
|
|
9
10
|
const results = await Promise.all(entries.map(async ([name, probe]) => [name, await runProbe(probe, timeoutMs)]));
|
package/dist/keys/client.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface HttpKeysOptions {
|
|
|
4
4
|
readonly url: string;
|
|
5
5
|
/** `NYLORUN_GATES_TOKEN`. */
|
|
6
6
|
readonly token: string;
|
|
7
|
+
/** Default `keys.timeoutMs` (Host tuning): signing and vault writes are short. */
|
|
7
8
|
readonly timeoutMs?: number;
|
|
8
9
|
}
|
|
9
10
|
export declare function httpKeys(options: HttpKeysOptions): Keys;
|
package/dist/keys/client.js
CHANGED
|
@@ -10,12 +10,11 @@ import { HttpError } from "../tenant/http.js";
|
|
|
10
10
|
import { VaultError } from "../vault/error.js";
|
|
11
11
|
import { KEYS_PATH } from "./contract.js";
|
|
12
12
|
import { KEYS_OPERATIONS } from "./keys.js";
|
|
13
|
-
|
|
14
|
-
const KEYS_TIMEOUT_MS = 30_000;
|
|
13
|
+
import { hostTuning } from "../tuning.js";
|
|
15
14
|
export function httpKeys(options) {
|
|
16
15
|
const where = new URL(options.url).origin;
|
|
17
16
|
function call(operation, args) {
|
|
18
|
-
const timeoutMs = options.timeoutMs ??
|
|
17
|
+
const timeoutMs = options.timeoutMs ?? hostTuning().keys.timeoutMs;
|
|
19
18
|
const url = new URL(`${KEYS_PATH}/${operation}`, options.url);
|
|
20
19
|
const send = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
21
20
|
const payload = Buffer.from(JSON.stringify({ args }), "utf8");
|
package/dist/keys/contract.d.ts
CHANGED
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
* exactly what it answered when the operation ran in its own process.
|
|
9
9
|
*/
|
|
10
10
|
export declare const KEYS_PATH = "/nylorun/v1/keys";
|
|
11
|
-
/** Largest request body: a credential with its secret, or a token's claims. */
|
|
12
|
-
export declare const MAX_KEYS_BODY_BYTES: number;
|
|
13
11
|
export interface KeysError {
|
|
14
12
|
readonly kind: "vault" | "http";
|
|
15
13
|
readonly status: number;
|
package/dist/keys/contract.js
CHANGED
package/dist/keys/egress-ca.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { SessionStore } from "../store/types.js";
|
|
2
|
-
/** How long a leaf egress-gate presents is valid: one connection rarely lasts this long. */
|
|
3
|
-
export declare const EGRESS_LEAF_MINUTES = 60;
|
|
4
2
|
export declare class EgressAuthority {
|
|
5
3
|
private readonly store;
|
|
6
4
|
private readonly kek;
|
package/dist/keys/egress-ca.js
CHANGED
|
@@ -10,8 +10,6 @@ import { X509Certificate } from "node:crypto";
|
|
|
10
10
|
import { decryptSecret, encryptSecret } from "../vault/crypto.js";
|
|
11
11
|
import { createCertificateAuthority, issueLeafCertificate } from "./x509.js";
|
|
12
12
|
const SETTING = "egress.ca";
|
|
13
|
-
/** How long a leaf egress-gate presents is valid: one connection rarely lasts this long. */
|
|
14
|
-
export const EGRESS_LEAF_MINUTES = 60;
|
|
15
13
|
function aad(certificate) {
|
|
16
14
|
return Buffer.from(`nylorun.egress-ca:${new X509Certificate(certificate).fingerprint256}`);
|
|
17
15
|
}
|
|
@@ -30,7 +28,8 @@ export class EgressAuthority {
|
|
|
30
28
|
}
|
|
31
29
|
/** A leaf for `host` and the caller's P-256 public key (SPKI PEM); the caller keeps its key. */
|
|
32
30
|
async signLeaf(host, publicKey) {
|
|
33
|
-
|
|
31
|
+
// Valid for `keys.leafLifetimeMs` (Host tuning): one connection rarely lasts this long.
|
|
32
|
+
return issueLeafCertificate({ authority: await this.load(), host, publicKey });
|
|
34
33
|
}
|
|
35
34
|
async load() {
|
|
36
35
|
if (this.authority)
|
package/dist/keys/keys.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AgentSecretView, HostModelView, McpPreview, McpPreviewRequest, PutAgentSecretRequest, PutHostModelRequest, SelectHostModelRequest, SigningKeyView } from "@nylorun/core/contracts";
|
|
2
2
|
import type { SessionStore } from "../store/types.js";
|
|
3
3
|
import type { OutboundPolicy } from "../tenant/outbound.js";
|
|
4
4
|
import { type SigningKeys } from "../tenant/signing-keys.js";
|
|
@@ -13,9 +13,10 @@ export interface Signed {
|
|
|
13
13
|
/** The signing key's id, the token's `kid`. */
|
|
14
14
|
readonly keyId: string;
|
|
15
15
|
}
|
|
16
|
+
import { type SealedWebhookSecret, type WebhookDelivery } from "./webhooks.js";
|
|
16
17
|
export interface Keys {
|
|
17
|
-
/** `PUT /v1/
|
|
18
|
-
putEnvironmentSecret(environmentId: string, name: string, body:
|
|
18
|
+
/** `PUT /v1/agents/{id}/secrets/{name}` (`VaultService.putEnvironmentSecret`). */
|
|
19
|
+
putEnvironmentSecret(environmentId: string, name: string, body: PutAgentSecretRequest, actor: string): Promise<AgentSecretView>;
|
|
19
20
|
putHostModel(body: PutHostModelRequest): Promise<HostModelView>;
|
|
20
21
|
selectHostModel(body: SelectHostModelRequest): Promise<HostModelView>;
|
|
21
22
|
/**
|
|
@@ -46,9 +47,19 @@ export interface Keys {
|
|
|
46
47
|
* egress CA (R2c). The leaf's private key never reaches keys; the CA's never leaves it.
|
|
47
48
|
*/
|
|
48
49
|
signEgressLeaf(host: string, publicKey: string): Promise<string>;
|
|
50
|
+
/**
|
|
51
|
+
* A new Standard Webhooks secret for Agent `agentId`'s trigger `triggerId` (track T4, D75), and
|
|
52
|
+
* the same sealed with the vault key for the trigger's row. The secret leaves keys this once.
|
|
53
|
+
*/
|
|
54
|
+
makeWebhookSecret(agentId: string, triggerId: string): Promise<{
|
|
55
|
+
secret: string;
|
|
56
|
+
sealed: SealedWebhookSecret;
|
|
57
|
+
}>;
|
|
58
|
+
/** Whether a delivery to that trigger carries a signature its sealed secret made. */
|
|
59
|
+
verifyWebhook(agentId: string, triggerId: string, sealed: SealedWebhookSecret, delivery: WebhookDelivery): Promise<boolean>;
|
|
49
60
|
}
|
|
50
61
|
/** The operations, by the name the HTTP route carries. */
|
|
51
|
-
export declare const KEYS_OPERATIONS: readonly ["putEnvironmentSecret", "putHostModel", "selectHostModel", "sealEnvironmentSecretCopies", "sign", "rotateSigningKeys", "ensureSigningKeys", "previewMcp", "egressCa", "signEgressLeaf"];
|
|
62
|
+
export declare const KEYS_OPERATIONS: readonly ["putEnvironmentSecret", "putHostModel", "selectHostModel", "sealEnvironmentSecretCopies", "sign", "rotateSigningKeys", "ensureSigningKeys", "previewMcp", "egressCa", "signEgressLeaf", "makeWebhookSecret", "verifyWebhook"];
|
|
52
63
|
export type KeysOperation = (typeof KEYS_OPERATIONS)[number];
|
|
53
64
|
export interface InProcessKeysOptions {
|
|
54
65
|
readonly store: SessionStore;
|
package/dist/keys/keys.js
CHANGED
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
import { EgressAuthority } from "./egress-ca.js";
|
|
17
17
|
import { SignJWT } from "jose";
|
|
18
18
|
import { previewMcpServer } from "../mcp/preview.js";
|
|
19
|
+
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
19
20
|
import { signingKeyView } from "../tenant/signing-keys.js";
|
|
21
|
+
import { makeWebhookSecret, verifyWebhook } from "./webhooks.js";
|
|
22
|
+
import { VaultCryptoError } from "../vault/crypto.js";
|
|
20
23
|
/** The operations, by the name the HTTP route carries. */
|
|
21
24
|
export const KEYS_OPERATIONS = [
|
|
22
25
|
"putEnvironmentSecret",
|
|
@@ -29,6 +32,8 @@ export const KEYS_OPERATIONS = [
|
|
|
29
32
|
"previewMcp",
|
|
30
33
|
"egressCa",
|
|
31
34
|
"signEgressLeaf",
|
|
35
|
+
"makeWebhookSecret",
|
|
36
|
+
"verifyWebhook",
|
|
32
37
|
];
|
|
33
38
|
export function inProcessKeys(options) {
|
|
34
39
|
const { store, vault, signingKeys, kek } = options;
|
|
@@ -56,8 +61,24 @@ export function inProcessKeys(options) {
|
|
|
56
61
|
const key = kek();
|
|
57
62
|
await store.tx((t) => signingKeys.ensure(t, key));
|
|
58
63
|
},
|
|
59
|
-
previewMcp: (body) => previewMcpServer(body, {
|
|
64
|
+
previewMcp: async (body) => previewMcpServer(body, {
|
|
65
|
+
vault,
|
|
66
|
+
policy: options.policy ?? {},
|
|
67
|
+
limits: await store.tx((t) => readRuntimeLimits(t)),
|
|
68
|
+
}),
|
|
60
69
|
egressCa: () => egress.certificate(),
|
|
61
70
|
signEgressLeaf: (host, publicKey) => egress.signLeaf(host, publicKey),
|
|
71
|
+
makeWebhookSecret: async (agentId, triggerId) => makeWebhookSecret(kek(), agentId, triggerId),
|
|
72
|
+
verifyWebhook: async (agentId, triggerId, sealed, delivery) => {
|
|
73
|
+
try {
|
|
74
|
+
return verifyWebhook(kek(), agentId, triggerId, sealed, delivery);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// A secret sealed with another vault key, or not one: it verifies nothing.
|
|
78
|
+
if (error instanceof VaultCryptoError)
|
|
79
|
+
return false;
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
62
83
|
};
|
|
63
84
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** A secret sealed with the vault key, as the trigger's row keeps it. */
|
|
2
|
+
export interface SealedWebhookSecret {
|
|
3
|
+
readonly nonce: string;
|
|
4
|
+
readonly ciphertext: string;
|
|
5
|
+
readonly wrappedDek: string;
|
|
6
|
+
readonly kekId: string;
|
|
7
|
+
}
|
|
8
|
+
/** What a delivery says, as it arrived: its headers, and its body in base64. */
|
|
9
|
+
export interface WebhookDelivery {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly timestamp: string;
|
|
12
|
+
readonly signature: string;
|
|
13
|
+
readonly body: string;
|
|
14
|
+
}
|
|
15
|
+
/** A new secret for Agent `agentId`'s trigger `triggerId`, and the same sealed for its row. */
|
|
16
|
+
export declare function makeWebhookSecret(kek: Buffer, agentId: string, triggerId: string): {
|
|
17
|
+
secret: string;
|
|
18
|
+
sealed: SealedWebhookSecret;
|
|
19
|
+
};
|
|
20
|
+
/** The signature of `delivery`'s id, timestamp and body with `key` (base64). */
|
|
21
|
+
export declare function signWebhook(key: Buffer, id: string, timestamp: string, body: Buffer): string;
|
|
22
|
+
/** The key bytes of a `whsec_` secret. */
|
|
23
|
+
export declare function webhookKeyOf(secret: string): Buffer;
|
|
24
|
+
/** Whether `delivery` carries a signature the sealed secret made. */
|
|
25
|
+
export declare function verifyWebhook(kek: Buffer, agentId: string, triggerId: string, sealed: SealedWebhookSecret, delivery: WebhookDelivery): boolean;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook signing secrets (track T4, D75): the keys service makes a trigger's Standard Webhooks
|
|
3
|
+
* secret, seals it with the Tenant's vault key for the trigger's row, and is the only place that
|
|
4
|
+
* opens it, to verify a delivery. The secret leaves keys once, when it is made, for its owner.
|
|
5
|
+
*
|
|
6
|
+
* Standard Webhooks (https://www.standardwebhooks.com): the secret is `whsec_` and the base64 of
|
|
7
|
+
* its key bytes; a delivery's `webhook-signature` is space-separated `v1,<base64>` entries, each an
|
|
8
|
+
* HMAC-SHA256 with the key over `<webhook-id>.<webhook-timestamp>.<body>`. One that matches, in
|
|
9
|
+
* constant time, verifies it. The timestamp's tolerance is the route's to check.
|
|
10
|
+
*/
|
|
11
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
12
|
+
import { decryptSecret, encryptSecret } from "../vault/crypto.js";
|
|
13
|
+
/** The Standard Webhooks secret format: the prefix, and 32 random bytes of key. */
|
|
14
|
+
const SECRET_PREFIX = "whsec_";
|
|
15
|
+
const SECRET_BYTES = 32;
|
|
16
|
+
const aadOf = (agentId, triggerId) => Buffer.from(`nylorun-webhook:${agentId}:${triggerId}`, "utf8");
|
|
17
|
+
/** A new secret for Agent `agentId`'s trigger `triggerId`, and the same sealed for its row. */
|
|
18
|
+
export function makeWebhookSecret(kek, agentId, triggerId) {
|
|
19
|
+
const key = randomBytes(SECRET_BYTES);
|
|
20
|
+
try {
|
|
21
|
+
const sealed = encryptSecret(kek, aadOf(agentId, triggerId), key);
|
|
22
|
+
return {
|
|
23
|
+
secret: `${SECRET_PREFIX}${key.toString("base64")}`,
|
|
24
|
+
sealed: {
|
|
25
|
+
nonce: sealed.nonce.toString("base64"),
|
|
26
|
+
ciphertext: sealed.ciphertext.toString("base64"),
|
|
27
|
+
wrappedDek: sealed.wrappedDek.toString("base64"),
|
|
28
|
+
kekId: sealed.kekId,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
key.fill(0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** The signature of `delivery`'s id, timestamp and body with `key` (base64). */
|
|
37
|
+
export function signWebhook(key, id, timestamp, body) {
|
|
38
|
+
return createHmac("sha256", key).update(`${id}.${timestamp}.`).update(body).digest("base64");
|
|
39
|
+
}
|
|
40
|
+
/** The key bytes of a `whsec_` secret. */
|
|
41
|
+
export function webhookKeyOf(secret) {
|
|
42
|
+
return Buffer.from(secret.startsWith(SECRET_PREFIX) ? secret.slice(SECRET_PREFIX.length) : secret, "base64");
|
|
43
|
+
}
|
|
44
|
+
/** Whether `delivery` carries a signature the sealed secret made. */
|
|
45
|
+
export function verifyWebhook(kek, agentId, triggerId, sealed, delivery) {
|
|
46
|
+
const key = decryptSecret(kek, aadOf(agentId, triggerId), Buffer.from(sealed.nonce, "base64"), Buffer.from(sealed.ciphertext, "base64"), Buffer.from(sealed.wrappedDek, "base64"), sealed.kekId);
|
|
47
|
+
try {
|
|
48
|
+
const expected = Buffer.from(signWebhook(key, delivery.id, delivery.timestamp, Buffer.from(delivery.body, "base64")), "base64");
|
|
49
|
+
for (const entry of delivery.signature.split(" ")) {
|
|
50
|
+
const [version, value] = entry.split(",", 2);
|
|
51
|
+
if (version !== "v1" || !value)
|
|
52
|
+
continue;
|
|
53
|
+
const given = Buffer.from(value, "base64");
|
|
54
|
+
if (given.length === expected.length && timingSafeEqual(given, expected))
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
key.fill(0);
|
|
61
|
+
}
|
|
62
|
+
}
|
package/dist/keys/x509.d.ts
CHANGED
|
@@ -4,22 +4,26 @@ export interface CertificateAuthority {
|
|
|
4
4
|
/** PKCS#8 PEM of the CA key: sealed in the vault, used only by keys. */
|
|
5
5
|
readonly privateKey: string;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* A new P-256 CA, valid from `keys.caBackdateMs` ago (Host tuning, an hour) for `days` days
|
|
9
|
+
* (default `keys.caLifetimeDays`).
|
|
10
|
+
*/
|
|
8
11
|
export declare function createCertificateAuthority(options: {
|
|
9
12
|
readonly commonName: string;
|
|
10
13
|
readonly days?: number;
|
|
11
14
|
readonly now?: Date;
|
|
12
15
|
}): CertificateAuthority;
|
|
13
16
|
/**
|
|
14
|
-
* A leaf for `host` and the caller's `publicKey` (SPKI PEM), signed by the CA, valid from
|
|
15
|
-
*
|
|
17
|
+
* A leaf for `host` and the caller's `publicKey` (SPKI PEM), signed by the CA, valid from
|
|
18
|
+
* `keys.leafBackdateMs` ago (Host tuning, five minutes) for `lifetimeMs` (default
|
|
19
|
+
* `keys.leafLifetimeMs`, an hour: one connection rarely lasts this long). The caller keeps the leaf's private key: only the
|
|
16
20
|
* certificate comes back, so no private key leaves whoever holds it.
|
|
17
21
|
*/
|
|
18
22
|
export declare function issueLeafCertificate(options: {
|
|
19
23
|
readonly authority: CertificateAuthority;
|
|
20
24
|
readonly host: string;
|
|
21
25
|
readonly publicKey: string;
|
|
22
|
-
readonly
|
|
26
|
+
readonly lifetimeMs?: number;
|
|
23
27
|
readonly now?: Date;
|
|
24
28
|
}): string;
|
|
25
29
|
/** A P-256 key pair for a leaf, as PEM: SPKI for the public key, PKCS#8 for the private key. */
|
package/dist/keys/x509.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* CA's key identifier. Go (`gh`), OpenSSL (`curl`, `git`), Node and Python accept that shape.
|
|
11
11
|
*/
|
|
12
12
|
import { isIP } from "node:net";
|
|
13
|
+
import { hostTuning } from "../tuning.js";
|
|
13
14
|
import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, randomBytes, sign, X509Certificate, } from "node:crypto";
|
|
14
15
|
const OID = {
|
|
15
16
|
ecdsaWithSha256: "1.2.840.10045.4.3.2",
|
|
@@ -96,21 +97,28 @@ function pem(der) {
|
|
|
96
97
|
const body = der.toString("base64").replace(/(.{64})/g, "$1\n").replace(/\n$/, "");
|
|
97
98
|
return `-----BEGIN CERTIFICATE-----\n${body}\n-----END CERTIFICATE-----\n`;
|
|
98
99
|
}
|
|
99
|
-
/**
|
|
100
|
+
/** Milliseconds in a day: a unit, not a setting. */
|
|
101
|
+
const DAY_MS = 86_400_000;
|
|
102
|
+
/**
|
|
103
|
+
* A new P-256 CA, valid from `keys.caBackdateMs` ago (Host tuning, an hour) for `days` days
|
|
104
|
+
* (default `keys.caLifetimeDays`).
|
|
105
|
+
*/
|
|
100
106
|
export function createCertificateAuthority(options) {
|
|
101
107
|
const { publicKey, privateKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
102
108
|
const now = options.now ?? new Date();
|
|
109
|
+
const tuning = hostTuning().keys;
|
|
103
110
|
const subject = name({ commonName: options.commonName, organization: "Nylorun" });
|
|
104
111
|
const ski = keyIdentifier(publicKey);
|
|
105
|
-
const tbs = seq(explicit(0, integer(Buffer.from([2]))), serial(), seq(oid(OID.ecdsaWithSha256)), subject, seq(time(new Date(now.getTime() -
|
|
112
|
+
const tbs = seq(explicit(0, integer(Buffer.from([2]))), serial(), seq(oid(OID.ecdsaWithSha256)), subject, seq(time(new Date(now.getTime() - tuning.caBackdateMs)), time(new Date(now.getTime() + (options.days ?? tuning.caLifetimeDays) * DAY_MS))), subject, spki(publicKey), explicit(3, seq(extension(OID.basicConstraints, true, seq(bool(true), integer(Buffer.from([0])))), extension(OID.keyUsage, true, bits(Buffer.from([0x06]), 1)), extension(OID.subjectKeyIdentifier, false, octets(ski)))));
|
|
106
113
|
return {
|
|
107
114
|
certificate: pem(certificate(tbs, privateKey)),
|
|
108
115
|
privateKey: privateKey.export({ type: "pkcs8", format: "pem" }).toString(),
|
|
109
116
|
};
|
|
110
117
|
}
|
|
111
118
|
/**
|
|
112
|
-
* A leaf for `host` and the caller's `publicKey` (SPKI PEM), signed by the CA, valid from
|
|
113
|
-
*
|
|
119
|
+
* A leaf for `host` and the caller's `publicKey` (SPKI PEM), signed by the CA, valid from
|
|
120
|
+
* `keys.leafBackdateMs` ago (Host tuning, five minutes) for `lifetimeMs` (default
|
|
121
|
+
* `keys.leafLifetimeMs`, an hour: one connection rarely lasts this long). The caller keeps the leaf's private key: only the
|
|
114
122
|
* certificate comes back, so no private key leaves whoever holds it.
|
|
115
123
|
*/
|
|
116
124
|
export function issueLeafCertificate(options) {
|
|
@@ -122,8 +130,9 @@ export function issueLeafCertificate(options) {
|
|
|
122
130
|
if (publicKey.asymmetricKeyType !== "ec" || publicKey.asymmetricKeyDetails?.namedCurve !== "prime256v1")
|
|
123
131
|
throw new Error("A leaf key must be ECDSA P-256");
|
|
124
132
|
const now = options.now ?? new Date();
|
|
133
|
+
const tuning = hostTuning().keys;
|
|
125
134
|
const issuer = Buffer.from(ca.raw);
|
|
126
|
-
const tbs = seq(explicit(0, integer(Buffer.from([2]))), serial(), seq(oid(OID.ecdsaWithSha256)), subjectOf(issuer), seq(time(new Date(now.getTime() -
|
|
135
|
+
const tbs = seq(explicit(0, integer(Buffer.from([2]))), serial(), seq(oid(OID.ecdsaWithSha256)), subjectOf(issuer), seq(time(new Date(now.getTime() - tuning.leafBackdateMs)), time(new Date(now.getTime() + (options.lifetimeMs ?? tuning.leafLifetimeMs)))), name({ commonName: options.host }), spki(publicKey), explicit(3, seq(extension(OID.keyUsage, true, bits(Buffer.from([0x80]), 7)), extension(OID.extKeyUsage, false, seq(oid(OID.serverAuth))), extension(OID.subjectAltName, false, seq(tlv(0x82, Buffer.from(options.host, "ascii")))), extension(OID.authorityKeyIdentifier, false, seq(tlv(0x80, keyIdentifier(ca.publicKey)))))));
|
|
127
136
|
return pem(certificate(tbs, caKey));
|
|
128
137
|
}
|
|
129
138
|
/** A P-256 key pair for a leaf, as PEM: SPKI for the public key, PKCS#8 for the private key. */
|