run402 1.55.0 → 1.57.0

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/README.md CHANGED
@@ -57,10 +57,13 @@ run402 projects schema <id> # introspect tables + R
57
57
  ```bash
58
58
  run402 sites deploy-dir ./dist # incremental upload (plan/commit transport)
59
59
  run402 deploy --manifest app.json # one-call full stack deploy
60
+ run402 deploy release active # inspect current-live release inventory
61
+ run402 deploy release diff --from empty --to active
60
62
  run402 subdomains claim my-app # → my-app.run402.com (auto-reassigns on next deploy)
61
63
  ```
62
64
 
63
65
  `deploy-dir` hashes each file client-side and only uploads bytes the gateway doesn't already have. Re-deploying an unchanged tree returns immediately with `bytes_uploaded: 0`. Progress events stream to stderr.
66
+ Release inspection commands print `{ status: "ok", release: ... }` or `{ status: "ok", diff: ... }`; use them after deploys to compare release inventory without starting another mutation.
64
67
 
65
68
  ### GitHub Actions OIDC deploys
66
69
 
@@ -119,6 +122,16 @@ import { db, adminDb, getUser, email, ai } from "@run402/functions";
119
122
 
120
123
  `db(req)` is the caller-context client (RLS applies); `adminDb()` bypasses RLS for platform-authored writes.
121
124
 
125
+ ### Secrets
126
+
127
+ ```bash
128
+ run402 secrets set <id> OPENAI_API_KEY --file ./.secrets/openai-key
129
+ run402 secrets list <id>
130
+ run402 deploy apply --manifest run402.deploy.json # manifest uses secrets.require, not values
131
+ ```
132
+
133
+ Secret values are write-only. `list` returns keys and timestamps only; deploy manifests should declare dependencies with `secrets.require` and never contain values.
134
+
122
135
  ### Email
123
136
 
124
137
  ```bash
package/lib/deploy-v2.mjs CHANGED
@@ -11,7 +11,7 @@
11
11
  * "project_id": "...",
12
12
  * "base": { "release": "current" } | { "release": "empty" } | { "release_id": "..." },
13
13
  * "database": { "migrations": [...], "expose": {...}, "zero_downtime": false },
14
- * "secrets": { "set": {...}, "delete": [...], "replace_all": {...} },
14
+ * "secrets": { "require": ["OPENAI_API_KEY"], "delete": ["OLD_KEY"] },
15
15
  * "functions": { "replace": {...}, "patch": { "set": {...}, "delete": [...] } },
16
16
  * "site": { "replace": {...} } | { "patch": { "put": {...}, "delete": [...] } },
17
17
  * "subdomains": { "set": ["..."], "add": [...], "remove": [...] },
@@ -33,8 +33,8 @@ import { API, allowanceAuthHeaders, getActiveProjectId, resolveProjectId } from
33
33
  const APPLY_HELP = `run402 deploy apply — Unified deploy primitive (v1.34+)
34
34
 
35
35
  Usage:
36
- run402 deploy apply --manifest <path> [--project <id>] [--quiet]
37
- run402 deploy apply --spec '<json>' [--project <id>] [--quiet]
36
+ run402 deploy apply --manifest <path> [--project <id>] [--quiet] [--allow-warnings]
37
+ run402 deploy apply --spec '<json>' [--project <id>] [--quiet] [--allow-warnings]
38
38
  cat spec.json | run402 deploy apply [--project <id>]
39
39
 
40
40
  Manifest format mirrors the MCP \`deploy\` tool's ReleaseSpec:
@@ -42,7 +42,7 @@ Manifest format mirrors the MCP \`deploy\` tool's ReleaseSpec:
42
42
  "project_id": "prj_...",
43
43
  "base": { "release": "current" },
44
44
  "database": { "migrations": [{ "id": "001_init", "sql": "CREATE TABLE ..." }], "expose": {...} },
45
- "secrets": { "set": { "OPENAI_API_KEY": { "value": "sk-..." } } },
45
+ "secrets": { "require": ["OPENAI_API_KEY"], "delete": ["OLD_KEY"] },
46
46
  "functions": { "replace": { "api": { "source": { "data": "export default ..." } } } },
47
47
  "site": { "replace": { "index.html": { "data": "<html>..." } } },
48
48
  "subdomains": { "set": ["my-app"] }
@@ -53,11 +53,18 @@ Options:
53
53
  --spec '<json>' Inline JSON spec (single-quote in shell)
54
54
  --project <id> Override project_id from the manifest
55
55
  --quiet Suppress per-event JSON-line stderr (final result still on stdout)
56
+ --allow-warnings Continue past plan warnings that require confirmation
56
57
 
57
58
  Output:
58
- stdout: { "status": "ok", "release_id": "rel_...", "operation_id": "op_...", "urls": {...} }
59
+ stdout: { "status": "ok", "release_id": "rel_...", "operation_id": "op_...", "urls": {...}, "warnings": [...] }
59
60
  stderr: one JSON event per line (suppressed with --quiet)
60
61
 
62
+ Secrets:
63
+ Secret values do not belong in deploy manifests. Set them first:
64
+ run402 secrets set prj_... OPENAI_API_KEY --file ./.secrets/openai-key
65
+ Then deploy a value-free declaration:
66
+ { "project_id": "prj_...", "secrets": { "require": ["OPENAI_API_KEY"] } }
67
+
61
68
  Patch examples (only the listed file changes):
62
69
  { "project_id": "prj_...", "site": { "patch": { "put": { "index.html": { "data": "..." } } } } }
63
70
  { "project_id": "prj_...", "site": { "patch": { "delete": ["old.html"] } } }
@@ -103,11 +110,70 @@ Output:
103
110
  stdout: { "status": "ok", "events": [...] }
104
111
  `;
105
112
 
113
+ const RELEASE_HELP = `run402 deploy release — Inspect deploy release inventory and diffs
114
+
115
+ Usage:
116
+ run402 deploy release get <release_id> [--project <id>] [--site-limit <n>]
117
+ run402 deploy release active [--project <id>] [--site-limit <n>]
118
+ run402 deploy release diff --from <empty|active|release_id> --to <active|release_id> [--project <id>] [--limit <n>]
119
+
120
+ Subcommands:
121
+ get Fetch the inventory for a specific release id
122
+ active Fetch the current-live release inventory for the project
123
+ diff Diff two release targets
124
+
125
+ Output:
126
+ get/active: { "status": "ok", "release": {...} }
127
+ diff: { "status": "ok", "diff": {...} }
128
+ `;
129
+
130
+ const RELEASE_GET_HELP = `run402 deploy release get — Fetch a release inventory by id
131
+
132
+ Usage:
133
+ run402 deploy release get <release_id> [--project <id>] [--site-limit <n>]
134
+
135
+ Options:
136
+ --project <id> Project ID that owns the release (default: active project)
137
+ --site-limit <n> Maximum site path entries to include (gateway default: 5000)
138
+
139
+ Output:
140
+ stdout: { "status": "ok", "release": {...} }
141
+ `;
142
+
143
+ const RELEASE_ACTIVE_HELP = `run402 deploy release active — Fetch the active release inventory
144
+
145
+ Usage:
146
+ run402 deploy release active [--project <id>] [--site-limit <n>]
147
+
148
+ Options:
149
+ --project <id> Project ID to inspect (default: active project)
150
+ --site-limit <n> Maximum site path entries to include (gateway default: 5000)
151
+
152
+ Output:
153
+ stdout: { "status": "ok", "release": {...} }
154
+ `;
155
+
156
+ const RELEASE_DIFF_HELP = `run402 deploy release diff — Diff two release targets
157
+
158
+ Usage:
159
+ run402 deploy release diff --from <empty|active|release_id> --to <active|release_id> [--project <id>] [--limit <n>]
160
+
161
+ Options:
162
+ --from <target> Diff source: empty, active, or rel_...
163
+ --to <target> Diff target: active or rel_...
164
+ --project <id> Project ID to inspect (default: active project)
165
+ --limit <n> Maximum entries per site diff bucket (gateway default: 1000)
166
+
167
+ Output:
168
+ stdout: { "status": "ok", "diff": {...} }
169
+ `;
170
+
106
171
  export async function runDeployV2(sub, args) {
107
172
  if (sub === "apply") return await applyCmd(args);
108
173
  if (sub === "resume") return await resumeCmd(args);
109
174
  if (sub === "list") return await listCmd(args);
110
175
  if (sub === "events") return await eventsCmd(args);
176
+ if (sub === "release") return await releaseCmd(args);
111
177
  fail({
112
178
  code: "BAD_USAGE",
113
179
  message: `Unknown deploy subcommand: ${sub}`,
@@ -129,13 +195,14 @@ function makeStderrEventWriter(quiet) {
129
195
  }
130
196
 
131
197
  async function applyCmd(args) {
132
- const opts = { manifest: null, spec: null, project: null, quiet: false };
198
+ const opts = { manifest: null, spec: null, project: null, quiet: false, allowWarnings: false };
133
199
  for (let i = 0; i < args.length; i++) {
134
200
  if (args[i] === "--help" || args[i] === "-h") { console.log(APPLY_HELP); process.exit(0); }
135
201
  if (args[i] === "--manifest" && args[i + 1]) { opts.manifest = args[++i]; continue; }
136
202
  if (args[i] === "--spec" && args[i + 1]) { opts.spec = args[++i]; continue; }
137
203
  if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
138
204
  if (args[i] === "--quiet") { opts.quiet = true; continue; }
205
+ if (args[i] === "--allow-warnings") { opts.allowWarnings = true; continue; }
139
206
  }
140
207
 
141
208
  let raw;
@@ -166,6 +233,10 @@ async function applyCmd(args) {
166
233
  details: { source: opts.manifest ? "manifest" : opts.spec ? "spec" : "stdin", parse_error: err.message },
167
234
  });
168
235
  }
236
+ rejectLegacySecretManifest(spec, {
237
+ source: opts.manifest ? "manifest" : opts.spec ? "spec" : "stdin",
238
+ ...(opts.manifest ? { path: resolve(opts.manifest) } : {}),
239
+ });
169
240
 
170
241
  // GH-232: Reject empty specs client-side. Without this guard,
171
242
  // `run402 deploy apply --spec '{}'` (and `--manifest <empty>`) would silently
@@ -241,6 +312,7 @@ async function applyCmd(args) {
241
312
  const result = await getSdk(sdkOpts).deploy.apply(releaseSpec, {
242
313
  onEvent: makeStderrEventWriter(opts.quiet),
243
314
  idempotencyKey,
315
+ allowWarnings: opts.allowWarnings,
244
316
  });
245
317
  console.log(JSON.stringify({ status: "ok", ...result }, null, 2));
246
318
  } catch (err) {
@@ -320,8 +392,43 @@ const CI_DEPLOY_ERROR_GUIDANCE = {
320
392
  };
321
393
 
322
394
  function reportDeployApplyError(err, useGithubActionsOidc) {
323
- if (!useGithubActionsOidc) return reportSdkError(err);
324
- return reportSdkError(enhanceCiDeployError(err));
395
+ const warningEnhanced = enhanceDeployWarningError(err);
396
+ if (!useGithubActionsOidc) return reportSdkError(warningEnhanced);
397
+ return reportSdkError(enhanceCiDeployError(warningEnhanced));
398
+ }
399
+
400
+ function enhanceDeployWarningError(err) {
401
+ const existingBody = err?.body && typeof err.body === "object" && !Array.isArray(err.body)
402
+ ? err.body
403
+ : {};
404
+ const warnings = Array.isArray(existingBody.warnings) ? existingBody.warnings : null;
405
+ const code = existingBody.code || err?.code || null;
406
+ if (!warnings && code !== "MISSING_REQUIRED_SECRET") return err;
407
+
408
+ const enhanced = Object.assign(new Error(err?.message || existingBody.message || String(code)), err);
409
+ const affected = warnings
410
+ ? warnings.flatMap((w) => Array.isArray(w?.affected) ? w.affected : [])
411
+ : [];
412
+ enhanced.body = {
413
+ ...existingBody,
414
+ code: code || "DEPLOY_WARNING_REQUIRES_CONFIRMATION",
415
+ message: existingBody.message || err?.message || "Deploy plan returned warnings that require confirmation.",
416
+ hint: existingBody.hint ||
417
+ (code === "MISSING_REQUIRED_SECRET"
418
+ ? "Set the missing secret values with `run402 secrets set`, then retry deploy apply. Use --allow-warnings only after explicit review."
419
+ : "Review the plan warnings, then retry with --allow-warnings if you intentionally accept them."),
420
+ next_actions: Array.isArray(existingBody.next_actions) && existingBody.next_actions.length > 0
421
+ ? existingBody.next_actions
422
+ : [
423
+ ...(affected.length > 0
424
+ ? [`Set or inspect affected secrets: ${Array.from(new Set(affected)).join(", ")}`]
425
+ : []),
426
+ "Retry `run402 deploy apply` after resolving warnings.",
427
+ "Use `--allow-warnings` only when the warning was explicitly reviewed.",
428
+ ],
429
+ ...(warnings ? { warnings } : {}),
430
+ };
431
+ return enhanced;
325
432
  }
326
433
 
327
434
  function enhanceCiDeployError(err) {
@@ -345,6 +452,37 @@ function enhanceCiDeployError(err) {
345
452
  return enhanced;
346
453
  }
347
454
 
455
+ function rejectLegacySecretManifest(spec, details) {
456
+ if (!spec || typeof spec !== "object" || Array.isArray(spec)) return;
457
+ const secrets = spec.secrets;
458
+ if (secrets === undefined) return;
459
+ if (Array.isArray(secrets) && secrets.length > 0) {
460
+ fail({
461
+ code: "UNSAFE_SECRET_MANIFEST",
462
+ message: "Deploy manifests must not contain secret values. Legacy secrets arrays are no longer supported by deploy apply.",
463
+ hint: "Run `run402 secrets set <project> <KEY> --file <path>` first, then use `\"secrets\": { \"require\": [\"KEY\"] }` in the deploy manifest.",
464
+ details: { ...details, field: "secrets", legacy_shape: "array" },
465
+ });
466
+ }
467
+ if (typeof secrets !== "object" || secrets === null) return;
468
+ if (Object.prototype.hasOwnProperty.call(secrets, "set")) {
469
+ fail({
470
+ code: "UNSAFE_SECRET_MANIFEST",
471
+ message: "Deploy manifests must not use secrets.set. Secret values are write-only and must be set outside deploy specs.",
472
+ hint: "Run `run402 secrets set <project> <KEY> --file <path>` first, then use `\"secrets\": { \"require\": [\"KEY\"] }`.",
473
+ details: { ...details, field: "secrets.set" },
474
+ });
475
+ }
476
+ if (Object.prototype.hasOwnProperty.call(secrets, "replace_all")) {
477
+ fail({
478
+ code: "UNSAFE_SECRET_MANIFEST",
479
+ message: "Deploy manifests must not use secrets.replace_all. Exact replacement is not representable in the value-free deploy contract.",
480
+ hint: "Use `secrets.require` for keys that must exist and `secrets.delete` for explicit removals.",
481
+ details: { ...details, field: "secrets.replace_all" },
482
+ });
483
+ }
484
+ }
485
+
348
486
  async function resumeCmd(args) {
349
487
  const opts = { operationId: null, quiet: false };
350
488
  for (let i = 0; i < args.length; i++) {
@@ -419,6 +557,118 @@ async function eventsCmd(args) {
419
557
  }
420
558
  }
421
559
 
560
+ async function releaseCmd(args) {
561
+ const action = args[0];
562
+ if (!action || action === "--help" || action === "-h") {
563
+ console.log(RELEASE_HELP);
564
+ process.exit(0);
565
+ }
566
+ if (action === "get") return await releaseGetCmd(args.slice(1));
567
+ if (action === "active") return await releaseActiveCmd(args.slice(1));
568
+ if (action === "diff") return await releaseDiffCmd(args.slice(1));
569
+ fail({
570
+ code: "BAD_USAGE",
571
+ message: `Unknown deploy release subcommand: ${action}`,
572
+ details: { subcommand: action },
573
+ });
574
+ }
575
+
576
+ async function releaseGetCmd(args) {
577
+ const opts = { releaseId: null, project: null, siteLimit: null };
578
+ for (let i = 0; i < args.length; i++) {
579
+ if (args[i] === "--help" || args[i] === "-h") { console.log(RELEASE_GET_HELP); process.exit(0); }
580
+ if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
581
+ if (args[i] === "--site-limit" && args[i + 1]) { opts.siteLimit = parsePositiveInt(args[++i], "--site-limit"); continue; }
582
+ if (!args[i].startsWith("-") && !opts.releaseId) opts.releaseId = args[i];
583
+ }
584
+ if (!opts.releaseId) {
585
+ fail({
586
+ code: "BAD_USAGE",
587
+ message: "Missing <release_id>.",
588
+ hint: "run402 deploy release get <release_id>",
589
+ });
590
+ }
591
+
592
+ const project = resolveProjectId(opts.project);
593
+
594
+ try {
595
+ const sdkOpts = { project };
596
+ if (opts.siteLimit !== null) sdkOpts.siteLimit = opts.siteLimit;
597
+ const release = await getSdk().deploy.getRelease(opts.releaseId, sdkOpts);
598
+ console.log(JSON.stringify({ status: "ok", release }, null, 2));
599
+ } catch (err) {
600
+ reportSdkError(err);
601
+ }
602
+ }
603
+
604
+ async function releaseActiveCmd(args) {
605
+ const opts = { project: null, siteLimit: null };
606
+ for (let i = 0; i < args.length; i++) {
607
+ if (args[i] === "--help" || args[i] === "-h") { console.log(RELEASE_ACTIVE_HELP); process.exit(0); }
608
+ if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
609
+ if (args[i] === "--site-limit" && args[i + 1]) { opts.siteLimit = parsePositiveInt(args[++i], "--site-limit"); continue; }
610
+ }
611
+
612
+ const project = resolveProjectId(opts.project);
613
+
614
+ try {
615
+ const sdkOpts = { project };
616
+ if (opts.siteLimit !== null) sdkOpts.siteLimit = opts.siteLimit;
617
+ const release = await getSdk().deploy.getActiveRelease(sdkOpts);
618
+ console.log(JSON.stringify({ status: "ok", release }, null, 2));
619
+ } catch (err) {
620
+ reportSdkError(err);
621
+ }
622
+ }
623
+
624
+ async function releaseDiffCmd(args) {
625
+ const opts = { project: null, from: null, to: null, limit: null };
626
+ for (let i = 0; i < args.length; i++) {
627
+ if (args[i] === "--help" || args[i] === "-h") { console.log(RELEASE_DIFF_HELP); process.exit(0); }
628
+ if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
629
+ if (args[i] === "--from" && args[i + 1]) { opts.from = args[++i]; continue; }
630
+ if (args[i] === "--to" && args[i + 1]) { opts.to = args[++i]; continue; }
631
+ if (args[i] === "--limit" && args[i + 1]) { opts.limit = parsePositiveInt(args[++i], "--limit"); continue; }
632
+ }
633
+ if (!opts.from || !opts.to) {
634
+ fail({
635
+ code: "BAD_USAGE",
636
+ message: "Missing --from or --to release target.",
637
+ hint: "run402 deploy release diff --from empty --to active",
638
+ });
639
+ }
640
+ if (opts.to === "empty") {
641
+ fail({
642
+ code: "BAD_USAGE",
643
+ message: "--to cannot be empty. Use active or a release id.",
644
+ details: { flag: "--to", value: opts.to },
645
+ });
646
+ }
647
+
648
+ const project = resolveProjectId(opts.project);
649
+
650
+ try {
651
+ const sdkOpts = { project, from: opts.from, to: opts.to };
652
+ if (opts.limit !== null) sdkOpts.limit = opts.limit;
653
+ const diff = await getSdk().deploy.diff(sdkOpts);
654
+ console.log(JSON.stringify({ status: "ok", diff }, null, 2));
655
+ } catch (err) {
656
+ reportSdkError(err);
657
+ }
658
+ }
659
+
660
+ function parsePositiveInt(value, flag) {
661
+ const parsed = Number(value);
662
+ if (!Number.isInteger(parsed) || parsed < 1) {
663
+ fail({
664
+ code: "BAD_USAGE",
665
+ message: `${flag} must be a positive integer.`,
666
+ details: { flag, value },
667
+ });
668
+ }
669
+ return parsed;
670
+ }
671
+
422
672
  // ─── Manifest → ReleaseSpec ──────────────────────────────────────────────────
423
673
 
424
674
  function mapManifestToReleaseSpec(spec) {
package/lib/deploy.mjs CHANGED
@@ -21,6 +21,7 @@ Subcommands (recommended for new manifests):
21
21
  run402 deploy resume <operation_id> resume a stuck operation
22
22
  run402 deploy list [--project <id>] list recent deploy operations
23
23
  run402 deploy events <operation_id> fetch event stream for an operation
24
+ run402 deploy release ... inspect release inventory and diffs
24
25
 
25
26
  Manifest format (JSON, v2 ReleaseSpec — recommended):
26
27
  {
@@ -36,7 +37,7 @@ Manifest format (JSON, v2 ReleaseSpec — recommended):
36
37
  ]
37
38
  }
38
39
  },
39
- "secrets": { "set": { "OPENAI_API_KEY": { "value": "sk-..." } } },
40
+ "secrets": { "require": ["OPENAI_API_KEY"], "delete": ["OLD_KEY"] },
40
41
  "functions": {
41
42
  "replace": {
42
43
  "api": {
@@ -63,9 +64,13 @@ Manifest format (JSON, v2 ReleaseSpec — recommended):
63
64
  Replace vs patch semantics per resource:
64
65
  "site": { "replace": {...} } whole-site (omitted files removed)
65
66
  "site": { "patch": { "put": {...}, "delete": [...] } } surgical updates
66
- Same for "functions" and "secrets". Migrations are always additive (each
67
- is keyed by id; re-shipping the same id+sql is a registry noop, same id
68
- with different sql is a hard MIGRATION_CHECKSUM_MISMATCH error).
67
+ Same for "functions". Secrets are value-free declarations:
68
+ "secrets": { "require": ["OPENAI_API_KEY"], "delete": ["OLD_KEY"] }
69
+ Secret values must be set outside deploy manifests with:
70
+ run402 secrets set prj_... OPENAI_API_KEY --file ./.secrets/openai-key
71
+ Migrations are always additive (each is keyed by id; re-shipping the same
72
+ id+sql is a registry noop, same id with different sql is a hard
73
+ MIGRATION_CHECKSUM_MISMATCH error).
69
74
 
70
75
  File entries accept inline "data", a local "path", or a "sql_path"
71
76
  (migrations only) — paths are resolved relative to the manifest file's
@@ -94,12 +99,14 @@ Manifest format (JSON, v2 ReleaseSpec — recommended):
94
99
  ⚠️ Without an "expose" entry, tables are unreachable via anon_key.
95
100
 
96
101
  Legacy v1 bundle format (still accepted via compatibility shim):
97
- Existing manifests with top-level "migrations" (string), "secrets" (array),
98
- "functions" (array), "files" (array), "subdomain" (string), and the
102
+ Existing manifests with top-level "migrations" (string), "functions" (array),
103
+ "files" (array), "subdomain" (string), and the
99
104
  "files[].file/data/path" + inline "manifest.json" entry continue to work —
100
105
  the SDK translates them into a v2 ReleaseSpec under the hood. Prefer the
101
106
  v2 shape above for new manifests; the legacy form is preserved for the
102
- deprecation window so existing scripts don't break.
107
+ deprecation window so existing scripts don't break. Legacy file manifests
108
+ with secret values no longer deploy: run 'run402 secrets set' first, then
109
+ use 'run402 deploy apply' with 'secrets.require'.
103
110
 
104
111
  "migrations_file": "setup.sql" (legacy convenience) reads SQL from disk
105
112
  relative to the manifest file. Useful when JSONB literals make inline
@@ -224,6 +231,11 @@ async function loadManifest(opts) {
224
231
  });
225
232
  }
226
233
 
234
+ rejectUnsafeSecretManifest(manifest, {
235
+ source: opts.manifest ? "manifest" : "stdin",
236
+ ...(opts.manifest ? { path: resolve(opts.manifest) } : {}),
237
+ });
238
+
227
239
  if (opts.manifest) {
228
240
  try {
229
241
  resolveMigrationsFile(manifest, baseDir);
@@ -255,19 +267,52 @@ async function loadManifest(opts) {
255
267
  return manifest;
256
268
  }
257
269
 
270
+ function rejectUnsafeSecretManifest(manifest, details) {
271
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return;
272
+ const secrets = manifest.secrets;
273
+ if (secrets === undefined) return;
274
+ if (Array.isArray(secrets) && secrets.length > 0) {
275
+ fail({
276
+ code: "UNSAFE_SECRET_MANIFEST",
277
+ message: "Deploy manifests must not contain secret values. Legacy top-level secrets arrays are no longer supported.",
278
+ hint: "Run `run402 secrets set <project> <KEY> --file <path>` first, then use `run402 deploy apply` with `\"secrets\": { \"require\": [\"KEY\"] }`.",
279
+ details: { ...details, field: "secrets", legacy_shape: "array" },
280
+ });
281
+ }
282
+ if (!secrets || typeof secrets !== "object" || Array.isArray(secrets)) return;
283
+ if (Object.prototype.hasOwnProperty.call(secrets, "set")) {
284
+ fail({
285
+ code: "UNSAFE_SECRET_MANIFEST",
286
+ message: "Deploy manifests must not use secrets.set. Secret values are write-only and must be set outside deploy specs.",
287
+ hint: "Run `run402 secrets set <project> <KEY> --file <path>` first, then deploy with `\"secrets\": { \"require\": [\"KEY\"] }`.",
288
+ details: { ...details, field: "secrets.set" },
289
+ });
290
+ }
291
+ if (Object.prototype.hasOwnProperty.call(secrets, "replace_all")) {
292
+ fail({
293
+ code: "UNSAFE_SECRET_MANIFEST",
294
+ message: "Deploy manifests must not use secrets.replace_all. Exact replacement is not representable in the value-free deploy contract.",
295
+ hint: "Use `secrets.require` for keys that must exist and `secrets.delete` for explicit removals.",
296
+ details: { ...details, field: "secrets.replace_all" },
297
+ });
298
+ }
299
+ }
300
+
258
301
  export async function run(args) {
259
302
  // Subcommand dispatch (v1.34+):
260
303
  // run402 deploy apply ... → unified deploy primitive (deploy.apply)
261
304
  // run402 deploy resume <op> → resume an activation_pending operation
262
305
  // run402 deploy list → list recent deploy operations
263
306
  // run402 deploy events <op> → fetch recorded event stream for an operation
307
+ // run402 deploy release ... → release inventory/diff observability
264
308
  // run402 deploy --manifest … → legacy bundle deploy (routes through v2)
265
309
  const sub = args[0];
266
310
  switch (sub) {
267
311
  case "apply":
268
312
  case "resume":
269
313
  case "list":
270
- case "events": {
314
+ case "events":
315
+ case "release": {
271
316
  const { runDeployV2 } = await import("./deploy-v2.mjs");
272
317
  await runDeployV2(sub, args.slice(1));
273
318
  return;
package/lib/secrets.mjs CHANGED
@@ -14,14 +14,15 @@ Subcommands:
14
14
  delete <id> <key> Delete a secret from a project
15
15
 
16
16
  Examples:
17
- run402 secrets set prj_abc123 STRIPE_KEY sk-1234
17
+ run402 secrets set prj_abc123 STRIPE_KEY --file ./.secrets/stripe-key
18
18
  run402 secrets set prj_abc123 TLS_CERT --file cert.pem
19
19
  run402 secrets list prj_abc123
20
20
  run402 secrets delete prj_abc123 STRIPE_KEY
21
21
 
22
22
  Notes:
23
23
  - Secrets are injected as process.env in serverless functions
24
- - Values are write-only — list returns keys with a value_hash (first 8 hex chars of SHA-256) for verifying the correct value was set
24
+ - Values are write-only — list returns keys and timestamps only
25
+ - Deploy manifests should declare existing keys with secrets.require; never put values in deploy specs
25
26
  `;
26
27
 
27
28
  const SUB_HELP = {
@@ -41,10 +42,11 @@ Options:
41
42
 
42
43
  Notes:
43
44
  - Secrets are injected as process.env in serverless functions
44
- - Values are write-only; 'list' returns a value_hash for verification
45
+ - Values are write-only; 'list' cannot verify values by hash
46
+ - Prefer --file for real secrets so values do not land in shell history
45
47
 
46
48
  Examples:
47
- run402 secrets set prj_abc123 STRIPE_KEY sk-1234
49
+ run402 secrets set prj_abc123 STRIPE_KEY --file ./.secrets/stripe-key
48
50
  run402 secrets set prj_abc123 TLS_CERT --file cert.pem
49
51
  `,
50
52
  list: `run402 secrets list — List all secrets for a project
@@ -56,8 +58,7 @@ Arguments:
56
58
  <id> Project ID (from 'run402 projects list')
57
59
 
58
60
  Notes:
59
- - Returns secret keys with a value_hash (first 8 hex chars of SHA-256)
60
- for verifying the correct value was set; raw values are write-only
61
+ - Returns secret keys and timestamps only; raw values and value-derived hashes are never returned
61
62
 
62
63
  Examples:
63
64
  run402 secrets list prj_abc123
@@ -103,7 +104,14 @@ async function set(projectId, key, args = []) {
103
104
  async function list(projectId) {
104
105
  try {
105
106
  const data = await getSdk().secrets.list(projectId);
106
- console.log(JSON.stringify(data, null, 2));
107
+ const sanitized = {
108
+ secrets: (data.secrets || []).map((s) => ({
109
+ key: s.key,
110
+ ...(s.created_at ? { created_at: s.created_at } : {}),
111
+ ...(s.updated_at ? { updated_at: s.updated_at } : {}),
112
+ })),
113
+ };
114
+ console.log(JSON.stringify(sanitized, null, 2));
107
115
  } catch (err) {
108
116
  reportSdkError(err);
109
117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "1.55.0",
3
+ "version": "1.57.0",
4
4
  "description": "CLI for Run402 — provision Postgres databases, deploy static sites, generate images, and manage wallets via x402 and MPP micropayments.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -130,5 +130,5 @@ export { Deploy } from "./namespaces/deploy.js";
130
130
  export { Ci, CI_AUDIENCE, CI_GITHUB_ACTIONS_ISSUER, CI_GITHUB_ACTIONS_PROVIDER, DEFAULT_CI_DELEGATION_CHAIN_ID, V1_CI_ALLOWED_ACTIONS, V1_CI_ALLOWED_EVENTS_DEFAULT, assertCiDeployableSpec, buildCiDelegationResourceUri, buildCiDelegationStatement, normalizeCiDelegationValues, validateCiNonce, validateCiSubjectMatch, } from "./namespaces/ci.js";
131
131
  export { ScopedRun402 } from "./scoped.js";
132
132
  export type { CiAllowedAction, CiAllowedEvent, CiBindingErrorCode, CiBindingRow, CiCreateBindingInput, CiDelegationValues, CiDeployErrorCode, CiErrorCode, CiListBindingsInput, CiListBindingsResult, CiProvider, CiTokenExchangeErrorCode, CiTokenExchangeInput, CiTokenExchangeRequestBody, CiTokenExchangeResponse, NormalizedCiDelegationValues, ParsedDelegation, } from "./namespaces/ci.types.js";
133
- export type { ApplyOptions, CommitResponse, CommitStatus, ContentRef, ContentSource, DatabaseSpec, DeployDiff, DeployEvent, DeployOperation, DeployResult, ExposeManifest, FileSet, FsFileSource, FunctionSpec, FunctionsSpec, MigrationSpec, MissingContent, OperationSnapshot, OperationStatus, PaymentRequiredHint, PlanRequest, PlanResponse, ReleaseSpec, RouteSpec, SecretsSpec, SiteSpec, SmokeCheck, StartOptions, SubdomainsSpec, } from "./namespaces/deploy.types.js";
133
+ export type { ApplyOptions, ActiveReleaseInventory, CommitResponse, CommitStatus, ContentRef, ContentSource, DatabaseSpec, DeployObservabilityWarningEntry, DeployDiff, DeployEvent, DeployOperation, DeployResult, ExposeManifest, FileSet, FsFileSource, FunctionSpec, FunctionsDiff, FunctionsSpec, LegacyWarningEntry, MigrationAppliedEntry, MigrationSpec, MissingContent, OperationSnapshot, OperationStatus, PaymentRequiredHint, PlanDiffEnvelope, PlanMigrationDiff, PlanRequest, PlanResponse, ReleaseDiffOptions, ReleaseDiffTarget, ReleaseDiffToTarget, ReleaseFunctionEntry, ReleaseInventory, ReleaseInventoryBase, ReleaseInventoryByIdOptions, ReleaseInventoryOptions, ReleaseInventoryStateKind, ReleaseInventoryStatus, ReleaseSnapshotInventory, ReleaseSpec, ReleaseToReleaseDiff, RouteSpec, SecretsSpec, SecretsDiff, SiteDiff, SitePathEntry, SiteSpec, SmokeCheck, StartOptions, SubdomainsSpec, SubdomainsDiff, WarningEntry, } from "./namespaces/deploy.types.js";
134
134
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,WAAW,EAAE,mBAAmB,CAAC;IACjC;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED,qBAAa,MAAM;;IACjB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,KAAK,EAAG,EAAE,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;gBAIJ,IAAI,EAAE,aAAa;IA6D/B;;;;;;;;;;;;;;;;OAgBG;IACG,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAsBjD;;;;;;;;;;;OAWG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;CAIpD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,OAAO,CAEpE;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAElD;AAED,OAAO,EACL,WAAW,EACX,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,cAAc,EACd,YAAY,EACZ,aAAa,EACb,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACzE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,2BAA2B,EAC3B,iCAAiC,EACjC,+BAA+B,GAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EACL,EAAE,EACF,WAAW,EACX,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,qBAAqB,EACrB,4BAA4B,EAC5B,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,EAC3B,eAAe,EACf,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EACV,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,wBAAwB,EACxB,oBAAoB,EACpB,0BAA0B,EAC1B,uBAAuB,EACvB,4BAA4B,EAC5B,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,YAAY,EACZ,sBAAsB,EACtB,cAAc,EACd,YAAY,EACZ,UAAU,EACV,aAAa,EACb,YAAY,EACZ,+BAA+B,EAC/B,UAAU,EACV,WAAW,EACX,eAAe,EACf,YAAY,EACZ,cAAc,EACd,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,WAAW,EACX,WAAW,EACX,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,YAAY,GACb,MAAM,8BAA8B,CAAC"}
@@ -5,6 +5,7 @@
5
5
  import type { Client } from "../kernel.js";
6
6
  import type { ProjectTier, RlsTemplate, RlsTableSpec } from "./projects.types.js";
7
7
  import type { SiteFile } from "./sites.js";
8
+ import type { WarningEntry } from "./deploy.types.js";
8
9
  export interface BundleRlsOptions {
9
10
  template: RlsTemplate;
10
11
  tables: RlsTableSpec[];
@@ -47,6 +48,7 @@ export interface BundleDeployResult {
47
48
  schedule?: string | null;
48
49
  }>;
49
50
  subdomain_url?: string;
51
+ warnings?: WarningEntry[];
50
52
  }
51
53
  export interface AppSummary {
52
54
  id: string;
@@ -128,10 +130,10 @@ export declare class Apps {
128
130
  private readonly client;
129
131
  constructor(client: Client);
130
132
  /**
131
- * Deploy to an existing project: runs migrations, applies RLS, sets
132
- * secrets, deploys functions, deploys a static site, and claims a
133
- * subdomain. Payment flows through x402 when the project lease needs
134
- * renewal.
133
+ * Deploy to an existing project: runs migrations, applies RLS, writes
134
+ * legacy in-memory secret values through the secrets API, deploys
135
+ * functions, deploys a static site, and claims a subdomain. Payment flows
136
+ * through x402 when the project lease needs renewal.
135
137
  *
136
138
  * **As of v1.34, this method is a thin compatibility shim over
137
139
  * {@link Deploy.apply}.** It translates the legacy bundle options into a
@@ -144,6 +146,11 @@ export declare class Apps {
144
146
  * the only behavior change vs. v1's blind re-execution and is safe for
145
147
  * idempotent migrations (the documented agent norm).
146
148
  *
149
+ * Legacy `opts.secrets` are not embedded in the release spec. They are
150
+ * pre-validated, written before deploy, then represented as
151
+ * `secrets.require` keys. Those writes are intentionally not atomic with
152
+ * the later deploy commit.
153
+ *
147
154
  * `inherit: true` is silently ignored;
148
155
  * patch semantics on `r.deploy.apply` replace it.
149
156
  */