@hasna/uptime 0.1.8 → 0.1.9

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 CHANGED
@@ -6,6 +6,15 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.9] - 2026-06-28
10
+
11
+ ### Changed
12
+
13
+ - AWS Terraform EFS mount targets now use stable list-index keys so deployment
14
+ roots can create private subnets and Open Uptime resources in one plan.
15
+ - AWS Terraform resources now include owner/project/environment/cost-center tags
16
+ and optional AWS Budgets alerts when recipients are configured.
17
+
9
18
  ## [0.1.8] - 2026-06-28
10
19
 
11
20
  ### Added
@@ -66,12 +75,12 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66
75
  - Dry-run AWS deployment plan generator for a reviewed AWS target,
67
76
  covering ECS/Fargate services, ECR image commands, ALB/RDS/S3/Secrets/Logs
68
77
  resources, rollback steps, and safety assertions.
69
- - Spark01 hosted-targeted private probe preflight config generator with JSON and
78
+ - Private-probe hosted-targeted preflight config generator with JSON and
70
79
  env-file rendering.
71
- - CLI commands `uptime cloud plan` and `uptime cloud spark01-config`.
80
+ - CLI commands `uptime cloud plan` and `uptime cloud private-probe-config`.
72
81
  - SDK export `@hasna/uptime/cloud-plan`.
73
82
  - Machine-readable `blocked`/`canApply:false` and `blocked`/`canStart:false`
74
- gates plus blocker/evidence lists for AWS and Spark01 planning artifacts.
83
+ gates plus blocker/evidence lists for AWS and private-probe planning artifacts.
75
84
 
76
85
  ### Security
77
86
 
package/README.md CHANGED
@@ -32,7 +32,7 @@ uptime report-schedules run-due
32
32
  uptime report-schedules runs
33
33
  uptime audit
34
34
  uptime cloud plan --json
35
- uptime cloud spark01-config --probe-id prb_spark01 --env
35
+ uptime cloud private-probe-config --probe-id prb_private_01 --machine-id private-probe-01 --env
36
36
  uptime incidents
37
37
  uptime serve --port 3899 --check
38
38
  ```
@@ -41,7 +41,7 @@ Scheduled reports persist endpoint and recipient configuration, but not send
41
41
  keys or API tokens. Configure `MAILERY_SEND_KEY`, `HASNA_MAILERY_SEND_KEY`,
42
42
  `HASNA_LOGS_API_TOKEN`, or the matching service env vars before scheduled runs.
43
43
 
44
- The `uptime cloud ...` commands generate dry-run AWS/Spark01 planning artifacts
44
+ The `uptime cloud ...` commands generate dry-run AWS/private-probe planning artifacts
45
45
  only. They do not call AWS, write secrets, or produce an approved deploy script;
46
46
  current output is intentionally blocked until the infra and cloud-store evidence
47
47
  in `docs/aws-deployment-runbook.md` is satisfied.
@@ -63,7 +63,7 @@ the published npm package into ECR from inside AWS.
63
63
  Private/local probes can submit signed results from another machine:
64
64
 
65
65
  ```bash
66
- uptime probes create spark01 --private-key-file ./spark01-probe.key.pem
66
+ uptime probes create private-probe-01 --private-key-file ./private-probe-01.key.pem
67
67
  uptime probes jobs create --monitor <monitor-id> --schedule-slot 2026-06-28T12:00:00Z
68
68
  uptime probes jobs claim <job-id> --probe <probe-id>
69
69
  uptime probes submit \
@@ -73,7 +73,7 @@ uptime probes submit \
73
73
  --fencing-token <claim-fencing-token> \
74
74
  --monitor <monitor-id> \
75
75
  --monitor-revision <claim-monitor-revision> \
76
- --private-key-file ./spark01-probe.key.pem \
76
+ --private-key-file ./private-probe-01.key.pem \
77
77
  --status up
78
78
  ```
79
79
 
package/dist/cli/index.js CHANGED
@@ -6478,7 +6478,7 @@ function buildAwsDeploymentPlan(options = {}) {
6478
6478
  const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
6479
6479
  const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
6480
6480
  const hostedSqliteDbPath = clean(options.hostedSqliteDbPath, DEFAULT_HOSTED_SQLITE_DB);
6481
- const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.8");
6481
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.9");
6482
6482
  const protectedAccessMode = options.protectedAccessMode ?? DEFAULT_PROTECTED_ACCESS_MODE;
6483
6483
  const protectedAccessUrl = protectedAccessMode === "cloudfront_default_domain" ? "https://<cloudfront-domain>" : `https://${hostname}`;
6484
6484
  const cluster = `${prefix}-${stage}`;
@@ -6610,9 +6610,9 @@ function buildAwsDeploymentPlan(options = {}) {
6610
6610
  "Disable scheduler/reporter services before data rollback.",
6611
6611
  "Restore EFS backup recovery point only after explicit operator approval and audit record."
6612
6612
  ],
6613
- spark01: [
6613
+ privateProbe: [
6614
6614
  "Create a private probe identity with a caller-managed public key.",
6615
- "Install @hasna/uptime on Spark01 and write the generated env file with mode 0600.",
6615
+ "Install @hasna/uptime on the private probe operator machine and write the generated env file with mode 0600.",
6616
6616
  "Run the private probe against the hosted /api/v1 probe endpoint once it exists."
6617
6617
  ]
6618
6618
  },
@@ -6621,7 +6621,7 @@ function buildAwsDeploymentPlan(options = {}) {
6621
6621
  "The EFS SQLite bridge is single-writer only: web target desired count is 1 and scheduler/public-probe/reporter targets remain 0 until Postgres and cloud leases exist.",
6622
6622
  "Hosted production auth/RBAC must replace broad static hosted-token operation before exposure.",
6623
6623
  "Public probe execution still needs DNS, redirect, and rebinding SSRF enforcement plus cloud check-job leases.",
6624
- "Spark01 hosted probe enrollment, claim, submit, heartbeat, revocation, and rotation are not cloud-backed yet."
6624
+ "Private probe enrollment, claim, submit, heartbeat, revocation, and rotation are not cloud-backed yet."
6625
6625
  ],
6626
6626
  requiredEvidence: [
6627
6627
  "Infrastructure PR/synth/plan from the approved infra repository.",
@@ -6631,7 +6631,7 @@ function buildAwsDeploymentPlan(options = {}) {
6631
6631
  "Single-writer ECS evidence: one web task maximum and no scheduler/public-probe/reporter EFS mounts.",
6632
6632
  "EFS encryption, access point, mount-target, AWS Backup, and restore-drill evidence.",
6633
6633
  "S3 bucket KMS, versioning, lifecycle, and public-access-block evidence.",
6634
- "Spark01 private-probe registration, key-file mode, heartbeat, and revocation evidence."
6634
+ "Private-probe registration, key-file mode, heartbeat, and revocation evidence."
6635
6635
  ],
6636
6636
  safety: {
6637
6637
  liveAwsMutation: false,
@@ -6649,16 +6649,16 @@ function buildAwsDeploymentPlan(options = {}) {
6649
6649
  }
6650
6650
  };
6651
6651
  }
6652
- function buildSpark01CloudConfig(options = {}) {
6652
+ function buildPrivateProbeCloudConfig(options = {}) {
6653
6653
  const apiUrl = clean(options.apiUrl, `https://${DEFAULT_HOSTNAME}/api/v1`);
6654
6654
  const workspaceId = clean(options.workspaceId, DEFAULT_WORKSPACE_ID);
6655
- const machineId = clean(options.machineId, "spark01");
6656
- const privateKeyFile = clean(options.probePrivateKeyFile, "~/.hasna/uptime/probes/spark01.key.pem");
6655
+ const machineId = clean(options.machineId, "private-probe-01");
6656
+ const privateKeyFile = clean(options.probePrivateKeyFile, "~/.hasna/uptime/probes/private-probe-01.key.pem");
6657
6657
  const probeId = options.probeId?.trim();
6658
6658
  const blockers = [
6659
6659
  ...probeId ? [] : ["Cloud-registered private probe id is required before writing a sourceable env file."],
6660
6660
  "Hosted probe claim and submit routes still fail closed until cloud check_jobs and workspace stores are implemented.",
6661
- "Spark01 enrollment, heartbeat, revocation, rotation, and bounded offline lease handling are not implemented yet."
6661
+ "Private probe enrollment, heartbeat, revocation, rotation, and bounded offline lease handling are not implemented yet."
6662
6662
  ];
6663
6663
  const env3 = {
6664
6664
  HASNA_UPTIME_MODE: "hosted",
@@ -6672,7 +6672,7 @@ function buildSpark01CloudConfig(options = {}) {
6672
6672
  if (probeId)
6673
6673
  env3.HASNA_UPTIME_PRIVATE_PROBE_ID = probeId;
6674
6674
  return {
6675
- kind: "open-uptime.spark01-cloud-config",
6675
+ kind: "open-uptime.private-probe-cloud-config",
6676
6676
  version: 1,
6677
6677
  generatedAt: new Date().toISOString(),
6678
6678
  status: "blocked",
@@ -6684,7 +6684,7 @@ function buildSpark01CloudConfig(options = {}) {
6684
6684
  {
6685
6685
  path: privateKeyFile,
6686
6686
  mode: "0600",
6687
- purpose: "Ed25519 private key generated on Spark01; never paste into cloud config."
6687
+ purpose: "Ed25519 private key generated on the private probe machine; never paste into cloud config."
6688
6688
  },
6689
6689
  {
6690
6690
  path: "~/.hasna/uptime/cloud.env",
@@ -6694,7 +6694,7 @@ function buildSpark01CloudConfig(options = {}) {
6694
6694
  ],
6695
6695
  commands: [
6696
6696
  "bun install -g @hasna/uptime@latest",
6697
- "Generate the Spark01 private key locally and register only its public key with the hosted control plane once registration exists.",
6697
+ "Generate the private probe key locally and register only its public key with the hosted control plane once registration exists.",
6698
6698
  "Write ~/.hasna/uptime/cloud.env from this plan, then source it for the private probe service.",
6699
6699
  "Start the private probe worker only after hosted /api/v1 probe claim/submit routes are backed by cloud jobs."
6700
6700
  ],
@@ -6703,18 +6703,18 @@ function buildSpark01CloudConfig(options = {}) {
6703
6703
  privateKeyInline: false,
6704
6704
  tokenInline: false,
6705
6705
  notes: [
6706
- "This config is hosted-targeted preflight: Spark01 must not start until cloud probe routes are backed by hosted state.",
6706
+ "This config is hosted-targeted preflight: the private probe must not start until cloud probe routes are backed by hosted state.",
6707
6707
  "The private key file path is referenced, not embedded.",
6708
6708
  "Hosted token or probe auth material must come from the machine secret store, not this generated config."
6709
6709
  ]
6710
6710
  }
6711
6711
  };
6712
6712
  }
6713
- function renderSpark01Env(config) {
6713
+ function renderPrivateProbeEnv(config) {
6714
6714
  const required = ["HASNA_UPTIME_PRIVATE_PROBE_ID"];
6715
6715
  const missing = required.filter((key) => !config.env[key]);
6716
6716
  if (missing.length > 0) {
6717
- throw new Error(`Spark01 env output requires ${missing.join(", ")}`);
6717
+ throw new Error(`private probe env output requires ${missing.join(", ")}`);
6718
6718
  }
6719
6719
  return Object.entries(config.env).map(([key, value]) => `${key}=${shellEscape(value)}`).join(`
6720
6720
  `);
@@ -7054,7 +7054,7 @@ program2.command("audit").description("List local audit events").option("--resou
7054
7054
  fail(error);
7055
7055
  }
7056
7056
  });
7057
- var cloud = program2.command("cloud").description("Generate dry-run cloud deployment and Spark01 configuration artifacts");
7057
+ var cloud = program2.command("cloud").description("Generate dry-run cloud deployment and private-probe configuration artifacts");
7058
7058
  cloud.command("plan").description("Generate a dry-run AWS deployment plan").option("--account <name>", "AWS account/profile label", "aws-profile").option("--region <region>", "AWS region", "us-east-1").option("--stage <stage>", "deployment stage", "prod").option("--hostname <hostname>", "hosted Open Uptime hostname", "uptime.example.com").option("--workspace-id <id>", "workspace id", "workspace-id").option("--vpc-id <id>", "target VPC id").option("--hosted-sqlite-db <path>", "hosted SQLite path on the EFS mount").option("--rds-instance-id <id>", "deprecated; ignored until the hosted Postgres adapter exists").option("--database-secret-name <name>", "deprecated; ignored until the hosted Postgres adapter exists").option("--ecr-repository <name>", "ECR repository name").option("--image <uri>", "container image URI").option("--runtime-package-version <version>", "published @hasna/uptime version for the AWS image builder").addOption(new Option("--protected-access-mode <mode>", "protected web access mode").choices(["cloudfront_default_domain", "alb_https_cert"]).default("cloudfront_default_domain")).option("--evidence-bucket <name>", "S3 evidence bucket name").option("-j, --json", "print JSON").action((opts) => {
7059
7059
  try {
7060
7060
  const plan = buildAwsDeploymentPlan({
@@ -7078,9 +7078,9 @@ cloud.command("plan").description("Generate a dry-run AWS deployment plan").opti
7078
7078
  fail(error);
7079
7079
  }
7080
7080
  });
7081
- cloud.command("spark01-config").description("Generate Spark01 hosted-targeted private probe preflight configuration").option("--api-url <url>", "hosted Open Uptime API URL", "https://uptime.example.com/api/v1").option("--workspace-id <id>", "workspace id", "workspace-id").option("--probe-id <id>", "cloud registered private probe id").option("--private-key-file <path>", "Spark01 private probe key file", "~/.hasna/uptime/probes/spark01.key.pem").option("--machine-id <id>", "machine id", "spark01").option("--log-level <level>", "probe log level", "info").option("--env", "print shell env file instead of summary text").option("-j, --json", "print JSON").action((opts) => {
7081
+ cloud.command("private-probe-config").description("Generate hosted-targeted private probe preflight configuration").option("--api-url <url>", "hosted Open Uptime API URL", "https://uptime.example.com/api/v1").option("--workspace-id <id>", "workspace id", "workspace-id").option("--probe-id <id>", "cloud registered private probe id").option("--private-key-file <path>", "private probe key file", "~/.hasna/uptime/probes/private-probe-01.key.pem").option("--machine-id <id>", "machine id", "private-probe-01").option("--log-level <level>", "probe log level", "info").option("--env", "print shell env file instead of summary text").option("-j, --json", "print JSON").action((opts) => {
7082
7082
  try {
7083
- const config = buildSpark01CloudConfig({
7083
+ const config = buildPrivateProbeCloudConfig({
7084
7084
  apiUrl: opts.apiUrl,
7085
7085
  workspaceId: opts.workspaceId,
7086
7086
  probeId: opts.probeId,
@@ -7089,10 +7089,10 @@ cloud.command("spark01-config").description("Generate Spark01 hosted-targeted pr
7089
7089
  logLevel: opts.logLevel
7090
7090
  });
7091
7091
  if (opts.env && !wantsJson(opts)) {
7092
- console.log(renderSpark01Env(config));
7092
+ console.log(renderPrivateProbeEnv(config));
7093
7093
  return;
7094
7094
  }
7095
- print(config, renderSpark01Config(config), opts);
7095
+ print(config, renderPrivateProbeConfig(config), opts);
7096
7096
  } catch (error) {
7097
7097
  fail(error);
7098
7098
  }
@@ -7472,7 +7472,7 @@ function renderCloudPlan(plan) {
7472
7472
  ].join(`
7473
7473
  `);
7474
7474
  }
7475
- function renderSpark01Config(config) {
7475
+ function renderPrivateProbeConfig(config) {
7476
7476
  return [
7477
7477
  `${config.machineId} ${config.mode} config`,
7478
7478
  `status: ${config.status}`,
@@ -75,7 +75,7 @@ export interface AwsDeploymentPlan {
75
75
  provision: string[];
76
76
  deploy: string[];
77
77
  rollback: string[];
78
- spark01: string[];
78
+ privateProbe: string[];
79
79
  };
80
80
  blockers: string[];
81
81
  requiredEvidence: string[];
@@ -98,7 +98,7 @@ export interface AwsServicePlan {
98
98
  environment: Record<string, string>;
99
99
  secrets: Record<string, string>;
100
100
  }
101
- export interface Spark01CloudConfigOptions {
101
+ export interface PrivateProbeCloudConfigOptions {
102
102
  apiUrl?: string;
103
103
  workspaceId?: string;
104
104
  probeId?: string;
@@ -106,8 +106,8 @@ export interface Spark01CloudConfigOptions {
106
106
  machineId?: string;
107
107
  logLevel?: string;
108
108
  }
109
- export interface Spark01CloudConfig {
110
- kind: "open-uptime.spark01-cloud-config";
109
+ export interface PrivateProbeCloudConfig {
110
+ kind: "open-uptime.private-probe-cloud-config";
111
111
  version: 1;
112
112
  generatedAt: string;
113
113
  status: "blocked";
@@ -129,6 +129,6 @@ export interface Spark01CloudConfig {
129
129
  };
130
130
  }
131
131
  export declare function buildAwsDeploymentPlan(options?: AwsDeploymentPlanOptions): AwsDeploymentPlan;
132
- export declare function buildSpark01CloudConfig(options?: Spark01CloudConfigOptions): Spark01CloudConfig;
133
- export declare function renderSpark01Env(config: Spark01CloudConfig): string;
132
+ export declare function buildPrivateProbeCloudConfig(options?: PrivateProbeCloudConfigOptions): PrivateProbeCloudConfig;
133
+ export declare function renderPrivateProbeEnv(config: PrivateProbeCloudConfig): string;
134
134
  //# sourceMappingURL=cloud-plan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-plan.d.ts","sourceRoot":"","sources":["../src/cloud-plan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cloud-plan.d.ts","sourceRoot":"","sources":["../src/cloud-plan.ts"],"names":[],"mappings":"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"}
@@ -21,7 +21,7 @@ function buildAwsDeploymentPlan(options = {}) {
21
21
  const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
22
22
  const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
23
23
  const hostedSqliteDbPath = clean(options.hostedSqliteDbPath, DEFAULT_HOSTED_SQLITE_DB);
24
- const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.8");
24
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.9");
25
25
  const protectedAccessMode = options.protectedAccessMode ?? DEFAULT_PROTECTED_ACCESS_MODE;
26
26
  const protectedAccessUrl = protectedAccessMode === "cloudfront_default_domain" ? "https://<cloudfront-domain>" : `https://${hostname}`;
27
27
  const cluster = `${prefix}-${stage}`;
@@ -153,9 +153,9 @@ function buildAwsDeploymentPlan(options = {}) {
153
153
  "Disable scheduler/reporter services before data rollback.",
154
154
  "Restore EFS backup recovery point only after explicit operator approval and audit record."
155
155
  ],
156
- spark01: [
156
+ privateProbe: [
157
157
  "Create a private probe identity with a caller-managed public key.",
158
- "Install @hasna/uptime on Spark01 and write the generated env file with mode 0600.",
158
+ "Install @hasna/uptime on the private probe operator machine and write the generated env file with mode 0600.",
159
159
  "Run the private probe against the hosted /api/v1 probe endpoint once it exists."
160
160
  ]
161
161
  },
@@ -164,7 +164,7 @@ function buildAwsDeploymentPlan(options = {}) {
164
164
  "The EFS SQLite bridge is single-writer only: web target desired count is 1 and scheduler/public-probe/reporter targets remain 0 until Postgres and cloud leases exist.",
165
165
  "Hosted production auth/RBAC must replace broad static hosted-token operation before exposure.",
166
166
  "Public probe execution still needs DNS, redirect, and rebinding SSRF enforcement plus cloud check-job leases.",
167
- "Spark01 hosted probe enrollment, claim, submit, heartbeat, revocation, and rotation are not cloud-backed yet."
167
+ "Private probe enrollment, claim, submit, heartbeat, revocation, and rotation are not cloud-backed yet."
168
168
  ],
169
169
  requiredEvidence: [
170
170
  "Infrastructure PR/synth/plan from the approved infra repository.",
@@ -174,7 +174,7 @@ function buildAwsDeploymentPlan(options = {}) {
174
174
  "Single-writer ECS evidence: one web task maximum and no scheduler/public-probe/reporter EFS mounts.",
175
175
  "EFS encryption, access point, mount-target, AWS Backup, and restore-drill evidence.",
176
176
  "S3 bucket KMS, versioning, lifecycle, and public-access-block evidence.",
177
- "Spark01 private-probe registration, key-file mode, heartbeat, and revocation evidence."
177
+ "Private-probe registration, key-file mode, heartbeat, and revocation evidence."
178
178
  ],
179
179
  safety: {
180
180
  liveAwsMutation: false,
@@ -192,16 +192,16 @@ function buildAwsDeploymentPlan(options = {}) {
192
192
  }
193
193
  };
194
194
  }
195
- function buildSpark01CloudConfig(options = {}) {
195
+ function buildPrivateProbeCloudConfig(options = {}) {
196
196
  const apiUrl = clean(options.apiUrl, `https://${DEFAULT_HOSTNAME}/api/v1`);
197
197
  const workspaceId = clean(options.workspaceId, DEFAULT_WORKSPACE_ID);
198
- const machineId = clean(options.machineId, "spark01");
199
- const privateKeyFile = clean(options.probePrivateKeyFile, "~/.hasna/uptime/probes/spark01.key.pem");
198
+ const machineId = clean(options.machineId, "private-probe-01");
199
+ const privateKeyFile = clean(options.probePrivateKeyFile, "~/.hasna/uptime/probes/private-probe-01.key.pem");
200
200
  const probeId = options.probeId?.trim();
201
201
  const blockers = [
202
202
  ...probeId ? [] : ["Cloud-registered private probe id is required before writing a sourceable env file."],
203
203
  "Hosted probe claim and submit routes still fail closed until cloud check_jobs and workspace stores are implemented.",
204
- "Spark01 enrollment, heartbeat, revocation, rotation, and bounded offline lease handling are not implemented yet."
204
+ "Private probe enrollment, heartbeat, revocation, rotation, and bounded offline lease handling are not implemented yet."
205
205
  ];
206
206
  const env = {
207
207
  HASNA_UPTIME_MODE: "hosted",
@@ -215,7 +215,7 @@ function buildSpark01CloudConfig(options = {}) {
215
215
  if (probeId)
216
216
  env.HASNA_UPTIME_PRIVATE_PROBE_ID = probeId;
217
217
  return {
218
- kind: "open-uptime.spark01-cloud-config",
218
+ kind: "open-uptime.private-probe-cloud-config",
219
219
  version: 1,
220
220
  generatedAt: new Date().toISOString(),
221
221
  status: "blocked",
@@ -227,7 +227,7 @@ function buildSpark01CloudConfig(options = {}) {
227
227
  {
228
228
  path: privateKeyFile,
229
229
  mode: "0600",
230
- purpose: "Ed25519 private key generated on Spark01; never paste into cloud config."
230
+ purpose: "Ed25519 private key generated on the private probe machine; never paste into cloud config."
231
231
  },
232
232
  {
233
233
  path: "~/.hasna/uptime/cloud.env",
@@ -237,7 +237,7 @@ function buildSpark01CloudConfig(options = {}) {
237
237
  ],
238
238
  commands: [
239
239
  "bun install -g @hasna/uptime@latest",
240
- "Generate the Spark01 private key locally and register only its public key with the hosted control plane once registration exists.",
240
+ "Generate the private probe key locally and register only its public key with the hosted control plane once registration exists.",
241
241
  "Write ~/.hasna/uptime/cloud.env from this plan, then source it for the private probe service.",
242
242
  "Start the private probe worker only after hosted /api/v1 probe claim/submit routes are backed by cloud jobs."
243
243
  ],
@@ -246,18 +246,18 @@ function buildSpark01CloudConfig(options = {}) {
246
246
  privateKeyInline: false,
247
247
  tokenInline: false,
248
248
  notes: [
249
- "This config is hosted-targeted preflight: Spark01 must not start until cloud probe routes are backed by hosted state.",
249
+ "This config is hosted-targeted preflight: the private probe must not start until cloud probe routes are backed by hosted state.",
250
250
  "The private key file path is referenced, not embedded.",
251
251
  "Hosted token or probe auth material must come from the machine secret store, not this generated config."
252
252
  ]
253
253
  }
254
254
  };
255
255
  }
256
- function renderSpark01Env(config) {
256
+ function renderPrivateProbeEnv(config) {
257
257
  const required = ["HASNA_UPTIME_PRIVATE_PROBE_ID"];
258
258
  const missing = required.filter((key) => !config.env[key]);
259
259
  if (missing.length > 0) {
260
- throw new Error(`Spark01 env output requires ${missing.join(", ")}`);
260
+ throw new Error(`private probe env output requires ${missing.join(", ")}`);
261
261
  }
262
262
  return Object.entries(config.env).map(([key, value]) => `${key}=${shellEscape(value)}`).join(`
263
263
  `);
@@ -290,7 +290,7 @@ function shellEscape(value) {
290
290
  return `'${value.replace(/'/g, "'\\''")}'`;
291
291
  }
292
292
  export {
293
- renderSpark01Env,
294
- buildSpark01CloudConfig,
293
+ renderPrivateProbeEnv,
294
+ buildPrivateProbeCloudConfig,
295
295
  buildAwsDeploymentPlan
296
296
  };
package/dist/index.d.ts CHANGED
@@ -5,13 +5,13 @@ export { createApiHandler, serveUptime } from "./api.js";
5
5
  export { applyImport, previewImport, rollbackImport } from "./imports.js";
6
6
  export { buildUptimeReport, sendUptimeReport } from "./report.js";
7
7
  export { generateProbeKeyPair, probePublicKeyFingerprint, probeResultSigningPayload, signProbeResult, verifyProbeResultSignature } from "./probes.js";
8
- export { buildAwsDeploymentPlan, buildSpark01CloudConfig, renderSpark01Env } from "./cloud-plan.js";
8
+ export { buildAwsDeploymentPlan, buildPrivateProbeCloudConfig, renderPrivateProbeEnv } from "./cloud-plan.js";
9
9
  export { uptimeHome, uptimeDbPath, uptimeHostedFallbackDbPath, ensureUptimeHome } from "./paths.js";
10
10
  export type { UptimeBackup, UptimeBackupCheck, UptimeRuntimeMode, UptimeStoreOptions, MonitorProvenance, SaveImportBatchInput, StoredImportBatch, UpsertMonitorProvenanceInput, } from "./store.js";
11
11
  export type { BrowserPageRunner, BrowserPageRunnerResult, FetchLike, } from "./checks.js";
12
12
  export type { ImportAction, ImportApplyItem, ImportApplyResult, ImportCandidate, ImportPreview, ImportPreviewItem, ImportRequest, ImportRollbackItem, ImportRollbackResult, ImportSource, } from "./imports.js";
13
13
  export type { BrowserFailedRequest, BrowserPageEvidence, AuditEvent, CheckAttemptResult, CheckEvidence, CheckResult, CheckStatus, CreateMonitorKind, CreateMonitorInput, CreateReportScheduleInput, ImportedMonitorInput, ImportedUpdateMonitorInput, EvidenceArtifact, Incident, IncidentStatus, ListAuditEventsOptions, ListReportRunsOptions, ListResultsOptions, Monitor, MonitorKind, MonitorStatus, MonitorSummary, ProbeCheckJob, ProbeCheckJobStatus, ProbeIdentity, ProbeResultSubmission, ProbeSubmissionReceipt, RecordAuditEventInput, ReportDeliveryChannel, ReportDeliveryRecord, ReportEmailChannelConfig, ReportLogsChannelConfig, ReportRun, ReportRunStatus, ReportSchedule, ReportScheduleChannels, ReportScheduleStatus, ReportSmsChannelConfig, SchedulerHandle, UpdateMonitorInput, UpdateReportScheduleInput, UptimeSummary, } from "./types.js";
14
14
  export type { ProbeKeyPair, ProbeSigningInput } from "./probes.js";
15
- export type { AwsDeploymentPlan, AwsDeploymentPlanOptions, AwsServicePlan, Spark01CloudConfig, Spark01CloudConfigOptions, } from "./cloud-plan.js";
15
+ export type { AwsDeploymentPlan, AwsDeploymentPlanOptions, AwsServicePlan, PrivateProbeCloudConfig, PrivateProbeCloudConfigOptions, } from "./cloud-plan.js";
16
16
  export type { BuildUptimeReportOptions, SendUptimeReportOptions, UptimeEmailReportTarget, UptimeLogsReportTarget, UptimeReport, UptimeReportDelivery, UptimeSmsReportTarget, } from "./report.js";
17
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9F,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACtJ,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACpG,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACpG,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,4BAA4B,GAC7B,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,SAAS,GACV,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,GACb,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,EAC1B,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,OAAO,EACP,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,SAAS,EACT,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9F,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACtJ,OAAO,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACpG,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,4BAA4B,GAC7B,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,SAAS,GACV,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,GACb,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,EAC1B,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,OAAO,EACP,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,SAAS,EACT,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,EACd,uBAAuB,EACvB,8BAA8B,GAC/B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -3881,7 +3881,7 @@ function buildAwsDeploymentPlan(options = {}) {
3881
3881
  const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
3882
3882
  const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
3883
3883
  const hostedSqliteDbPath = clean(options.hostedSqliteDbPath, DEFAULT_HOSTED_SQLITE_DB);
3884
- const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.8");
3884
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.9");
3885
3885
  const protectedAccessMode = options.protectedAccessMode ?? DEFAULT_PROTECTED_ACCESS_MODE;
3886
3886
  const protectedAccessUrl = protectedAccessMode === "cloudfront_default_domain" ? "https://<cloudfront-domain>" : `https://${hostname}`;
3887
3887
  const cluster = `${prefix}-${stage}`;
@@ -4013,9 +4013,9 @@ function buildAwsDeploymentPlan(options = {}) {
4013
4013
  "Disable scheduler/reporter services before data rollback.",
4014
4014
  "Restore EFS backup recovery point only after explicit operator approval and audit record."
4015
4015
  ],
4016
- spark01: [
4016
+ privateProbe: [
4017
4017
  "Create a private probe identity with a caller-managed public key.",
4018
- "Install @hasna/uptime on Spark01 and write the generated env file with mode 0600.",
4018
+ "Install @hasna/uptime on the private probe operator machine and write the generated env file with mode 0600.",
4019
4019
  "Run the private probe against the hosted /api/v1 probe endpoint once it exists."
4020
4020
  ]
4021
4021
  },
@@ -4024,7 +4024,7 @@ function buildAwsDeploymentPlan(options = {}) {
4024
4024
  "The EFS SQLite bridge is single-writer only: web target desired count is 1 and scheduler/public-probe/reporter targets remain 0 until Postgres and cloud leases exist.",
4025
4025
  "Hosted production auth/RBAC must replace broad static hosted-token operation before exposure.",
4026
4026
  "Public probe execution still needs DNS, redirect, and rebinding SSRF enforcement plus cloud check-job leases.",
4027
- "Spark01 hosted probe enrollment, claim, submit, heartbeat, revocation, and rotation are not cloud-backed yet."
4027
+ "Private probe enrollment, claim, submit, heartbeat, revocation, and rotation are not cloud-backed yet."
4028
4028
  ],
4029
4029
  requiredEvidence: [
4030
4030
  "Infrastructure PR/synth/plan from the approved infra repository.",
@@ -4034,7 +4034,7 @@ function buildAwsDeploymentPlan(options = {}) {
4034
4034
  "Single-writer ECS evidence: one web task maximum and no scheduler/public-probe/reporter EFS mounts.",
4035
4035
  "EFS encryption, access point, mount-target, AWS Backup, and restore-drill evidence.",
4036
4036
  "S3 bucket KMS, versioning, lifecycle, and public-access-block evidence.",
4037
- "Spark01 private-probe registration, key-file mode, heartbeat, and revocation evidence."
4037
+ "Private-probe registration, key-file mode, heartbeat, and revocation evidence."
4038
4038
  ],
4039
4039
  safety: {
4040
4040
  liveAwsMutation: false,
@@ -4052,16 +4052,16 @@ function buildAwsDeploymentPlan(options = {}) {
4052
4052
  }
4053
4053
  };
4054
4054
  }
4055
- function buildSpark01CloudConfig(options = {}) {
4055
+ function buildPrivateProbeCloudConfig(options = {}) {
4056
4056
  const apiUrl = clean(options.apiUrl, `https://${DEFAULT_HOSTNAME}/api/v1`);
4057
4057
  const workspaceId = clean(options.workspaceId, DEFAULT_WORKSPACE_ID);
4058
- const machineId = clean(options.machineId, "spark01");
4059
- const privateKeyFile = clean(options.probePrivateKeyFile, "~/.hasna/uptime/probes/spark01.key.pem");
4058
+ const machineId = clean(options.machineId, "private-probe-01");
4059
+ const privateKeyFile = clean(options.probePrivateKeyFile, "~/.hasna/uptime/probes/private-probe-01.key.pem");
4060
4060
  const probeId = options.probeId?.trim();
4061
4061
  const blockers = [
4062
4062
  ...probeId ? [] : ["Cloud-registered private probe id is required before writing a sourceable env file."],
4063
4063
  "Hosted probe claim and submit routes still fail closed until cloud check_jobs and workspace stores are implemented.",
4064
- "Spark01 enrollment, heartbeat, revocation, rotation, and bounded offline lease handling are not implemented yet."
4064
+ "Private probe enrollment, heartbeat, revocation, rotation, and bounded offline lease handling are not implemented yet."
4065
4065
  ];
4066
4066
  const env2 = {
4067
4067
  HASNA_UPTIME_MODE: "hosted",
@@ -4075,7 +4075,7 @@ function buildSpark01CloudConfig(options = {}) {
4075
4075
  if (probeId)
4076
4076
  env2.HASNA_UPTIME_PRIVATE_PROBE_ID = probeId;
4077
4077
  return {
4078
- kind: "open-uptime.spark01-cloud-config",
4078
+ kind: "open-uptime.private-probe-cloud-config",
4079
4079
  version: 1,
4080
4080
  generatedAt: new Date().toISOString(),
4081
4081
  status: "blocked",
@@ -4087,7 +4087,7 @@ function buildSpark01CloudConfig(options = {}) {
4087
4087
  {
4088
4088
  path: privateKeyFile,
4089
4089
  mode: "0600",
4090
- purpose: "Ed25519 private key generated on Spark01; never paste into cloud config."
4090
+ purpose: "Ed25519 private key generated on the private probe machine; never paste into cloud config."
4091
4091
  },
4092
4092
  {
4093
4093
  path: "~/.hasna/uptime/cloud.env",
@@ -4097,7 +4097,7 @@ function buildSpark01CloudConfig(options = {}) {
4097
4097
  ],
4098
4098
  commands: [
4099
4099
  "bun install -g @hasna/uptime@latest",
4100
- "Generate the Spark01 private key locally and register only its public key with the hosted control plane once registration exists.",
4100
+ "Generate the private probe key locally and register only its public key with the hosted control plane once registration exists.",
4101
4101
  "Write ~/.hasna/uptime/cloud.env from this plan, then source it for the private probe service.",
4102
4102
  "Start the private probe worker only after hosted /api/v1 probe claim/submit routes are backed by cloud jobs."
4103
4103
  ],
@@ -4106,18 +4106,18 @@ function buildSpark01CloudConfig(options = {}) {
4106
4106
  privateKeyInline: false,
4107
4107
  tokenInline: false,
4108
4108
  notes: [
4109
- "This config is hosted-targeted preflight: Spark01 must not start until cloud probe routes are backed by hosted state.",
4109
+ "This config is hosted-targeted preflight: the private probe must not start until cloud probe routes are backed by hosted state.",
4110
4110
  "The private key file path is referenced, not embedded.",
4111
4111
  "Hosted token or probe auth material must come from the machine secret store, not this generated config."
4112
4112
  ]
4113
4113
  }
4114
4114
  };
4115
4115
  }
4116
- function renderSpark01Env(config) {
4116
+ function renderPrivateProbeEnv(config) {
4117
4117
  const required = ["HASNA_UPTIME_PRIVATE_PROBE_ID"];
4118
4118
  const missing = required.filter((key) => !config.env[key]);
4119
4119
  if (missing.length > 0) {
4120
- throw new Error(`Spark01 env output requires ${missing.join(", ")}`);
4120
+ throw new Error(`private probe env output requires ${missing.join(", ")}`);
4121
4121
  }
4122
4122
  return Object.entries(config.env).map(([key, value]) => `${key}=${shellEscape(value)}`).join(`
4123
4123
  `);
@@ -4162,7 +4162,7 @@ export {
4162
4162
  runHttpCheck,
4163
4163
  runBrowserPageCheck,
4164
4164
  rollbackImport,
4165
- renderSpark01Env,
4165
+ renderPrivateProbeEnv,
4166
4166
  probeResultSigningPayload,
4167
4167
  probePublicKeyFingerprint,
4168
4168
  previewImport,
@@ -4171,7 +4171,7 @@ export {
4171
4171
  createUptimeClient,
4172
4172
  createApiHandler,
4173
4173
  buildUptimeReport,
4174
- buildSpark01CloudConfig,
4174
+ buildPrivateProbeCloudConfig,
4175
4175
  buildAwsDeploymentPlan,
4176
4176
  applyImport,
4177
4177
  UptimeStore,
@@ -8,7 +8,7 @@ call AWS or mutate infrastructure.
8
8
 
9
9
  ```bash
10
10
  uptime cloud plan --json > open-uptime-aws-plan.json
11
- uptime cloud spark01-config --probe-id prb_spark01 --env > spark01-uptime.env
11
+ uptime cloud private-probe-config --probe-id prb_private_01 --machine-id private-probe-01 --env > private-probe-01-uptime.env
12
12
  ```
13
13
 
14
14
  Public package defaults are placeholders:
@@ -25,7 +25,7 @@ Override these with CLI flags or private deployment evidence for the real
25
25
  account, hostname, workspace id, VPC id, secret refs, and repository names.
26
26
 
27
27
  The generated AWS plan currently returns `status: "blocked"` and
28
- `canApply: false`. The generated Spark01 config returns `status: "blocked"` and
28
+ `canApply: false`. The generated private-probe config returns `status: "blocked"` and
29
29
  `canStart: false`. Treat both as review/preflight artifacts until the blockers
30
30
  and required evidence in the JSON output are resolved.
31
31
 
@@ -33,7 +33,7 @@ The app repo includes a hosted runtime `Dockerfile` and Terraform/OpenTofu
33
33
  starter files in `infra/aws`. The plan output points to these files and keeps
34
34
  `applyAllowed: false`.
35
35
 
36
- `uptime cloud spark01-config --env` requires a real `--probe-id`; it will not
36
+ `uptime cloud private-probe-config --env` requires a real `--probe-id`; it will not
37
37
  write a sourceable env file with a placeholder probe identity.
38
38
 
39
39
  ## Preflight
@@ -89,12 +89,12 @@ terraform -chdir=infra/aws plan -out open-uptime.tfplan
89
89
 
90
90
  Use Terraform/OpenTofu 1.9 or newer for this starter.
91
91
 
92
- ## Spark01
92
+ ## Private Probe Operator
93
93
 
94
- Spark01 should be a private probe/operator machine, not the hosted source of
95
- truth. The generated env file points Spark01 at hosted `/api/v1` state and
96
- references a local private-key file path. It does not include private key or
97
- token contents.
94
+ The operator machine should be a private probe/operator machine, not the hosted
95
+ source of truth. The generated env file points the machine at hosted `/api/v1`
96
+ state and references a local private-key file path. It does not include private
97
+ key or token contents.
98
98
 
99
99
  The private probe service should not be enabled until hosted probe claim/submit
100
100
  routes are backed by cloud check jobs and cloud audit rows.
@@ -117,8 +117,14 @@ routes are backed by cloud check jobs and cloud audit rows.
117
117
  - Do not expose dashboard/API routes without hosted auth and workspace checks.
118
118
  - Do not expose the ALB directly in CloudFront mode; ALB ingress must be limited
119
119
  to CloudFront origin-facing ranges.
120
- - Do not treat local SQLite, local project DBs, or Spark01 local state as cloud
120
+ - Do not treat CloudFront prefix-list ingress as distribution-bound origin
121
+ protection. Before enabling the web task, add CloudFront VPC origin/private
122
+ ALB routing or require a CloudFront-only origin header whose secret value is
123
+ not stored in Terraform state.
124
+ - Do not treat local SQLite, local project DBs, or private-probe local state as cloud
121
125
  authority after cutover.
126
+ - Do configure owner/project/environment/service/cost-center tags and AWS
127
+ Budgets alert recipients in the approved infra root before live scale-out.
122
128
 
123
129
  ## Rollback
124
130
 
@@ -41,12 +41,23 @@ The web task receives `HASNA_UPTIME_ALLOWED_ORIGINS` for the selected public
41
41
  HTTPS origin so hosted mutation CSRF checks still work through the private HTTP
42
42
  origin hop.
43
43
 
44
+ CloudFront prefix-list ingress is only a network narrowing control; it is not
45
+ bound to one distribution. Add CloudFront VPC origin/private ALB routing or an
46
+ ALB origin-header rule with the secret value managed outside Terraform state
47
+ before enabling the web task.
48
+
49
+ All module resources carry owner, project, environment, service, account, app
50
+ type, and cost-center tags. Set `monthly_budget_limit_usd` plus
51
+ `budget_alert_email_addresses` in the approved infra root to create AWS Budgets
52
+ forecasted and actual spend alerts. Leaving the email list empty skips budget
53
+ creation and is not sufficient for live scale-out approval.
54
+
44
55
  ## Current Blockers
45
56
 
46
57
  - Hosted production auth/RBAC still needs scoped, revocable credentials.
47
58
  - Public probe runtime still needs execution-time DNS/redirect/rebinding SSRF
48
59
  enforcement.
49
- - Spark01 hosted private-probe enrollment/heartbeat/revocation is still
60
+ - Hosted private-probe enrollment/heartbeat/revocation is still
50
61
  fail-closed.
51
62
 
52
63
  Keep `desired_count` at `0`, or at `1` for the protected web bridge only after
package/infra/aws/main.tf CHANGED
@@ -53,10 +53,15 @@ locals {
53
53
  }
54
54
  }
55
55
  tags = {
56
- ManagedBy = "terraform"
57
- Service = var.service_name
58
- Stage = var.stage
59
- Account = var.account_name
56
+ ManagedBy = "terraform"
57
+ Service = var.service_name
58
+ Project = var.project_name
59
+ Stage = var.stage
60
+ Environment = var.environment
61
+ Account = var.account_name
62
+ Owner = var.owner
63
+ AppType = var.app_type
64
+ CostCenter = var.cost_center
60
65
  }
61
66
  }
62
67
 
@@ -436,7 +441,7 @@ resource "aws_efs_access_point" "uptime" {
436
441
  }
437
442
 
438
443
  resource "aws_efs_mount_target" "data" {
439
- for_each = toset(var.private_subnet_ids)
444
+ for_each = { for index, subnet_id in var.private_subnet_ids : tostring(index) => subnet_id }
440
445
  file_system_id = aws_efs_file_system.data.id
441
446
  subnet_id = each.value
442
447
  security_groups = [aws_security_group.efs.id]
@@ -529,6 +534,7 @@ resource "aws_lb_listener" "https" {
529
534
  port = 443
530
535
  protocol = "HTTPS"
531
536
  certificate_arn = var.certificate_arn
537
+ tags = local.tags
532
538
 
533
539
  default_action {
534
540
  type = "forward"
@@ -541,6 +547,7 @@ resource "aws_lb_listener" "http_cloudfront" {
541
547
  load_balancer_arn = aws_lb.open_uptime.arn
542
548
  port = 80
543
549
  protocol = "HTTP"
550
+ tags = local.tags
544
551
 
545
552
  default_action {
546
553
  type = "forward"
@@ -882,3 +889,35 @@ resource "aws_cloudwatch_metric_alarm" "web_unhealthy" {
882
889
  TargetGroup = aws_lb_target_group.web.arn_suffix
883
890
  }
884
891
  }
892
+
893
+ resource "aws_budgets_budget" "monthly" {
894
+ count = var.monthly_budget_limit_usd > 0 && length(var.budget_alert_email_addresses) > 0 ? 1 : 0
895
+ name = "${local.prefix}-monthly-budget"
896
+ budget_type = "COST"
897
+ limit_amount = format("%.2f", var.monthly_budget_limit_usd)
898
+ limit_unit = "USD"
899
+ time_unit = "MONTHLY"
900
+
901
+ cost_filter {
902
+ name = "TagKeyValue"
903
+ values = [format("user:Service$%s", var.service_name)]
904
+ }
905
+
906
+ notification {
907
+ comparison_operator = "GREATER_THAN"
908
+ notification_type = "FORECASTED"
909
+ threshold = 80
910
+ threshold_type = "PERCENTAGE"
911
+ subscriber_email_addresses = var.budget_alert_email_addresses
912
+ }
913
+
914
+ notification {
915
+ comparison_operator = "GREATER_THAN"
916
+ notification_type = "ACTUAL"
917
+ threshold = 100
918
+ threshold_type = "PERCENTAGE"
919
+ subscriber_email_addresses = var.budget_alert_email_addresses
920
+ }
921
+
922
+ tags = local.tags
923
+ }
@@ -1,6 +1,11 @@
1
1
  region = "us-east-1"
2
2
  stage = "prod"
3
3
  service_name = "open-uptime"
4
+ project_name = "open-uptime"
5
+ owner = "hasna"
6
+ app_type = "opensource"
7
+ environment = "prod"
8
+ cost_center = "opensource"
4
9
  hostname = "uptime.example.com"
5
10
  workspace_id = "workspace-id"
6
11
  vpc_id = "vpc-xxxxxxxx"
@@ -10,7 +15,7 @@ public_subnet_ids = ["subnet-replace-public-a", "subnet-replace-public-b"
10
15
  alb_ingress_cidr_blocks = []
11
16
  private_subnet_ids = ["subnet-replace-private-a", "subnet-replace-private-b"]
12
17
  container_image = "123456789012.dkr.ecr.us-east-1.amazonaws.com/open-uptime@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
13
- runtime_package_version = "0.1.8"
18
+ runtime_package_version = "0.1.9"
14
19
  certificate_arn = null
15
20
  hosted_zone_id = null
16
21
  app_env_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:open-uptime/prod/app/env"
@@ -19,6 +24,7 @@ public_probe_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret
19
24
  reporting_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:open-uptime/prod/reporting"
20
25
  kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/00000000-0000-0000-0000-000000000000"
21
26
  alarm_actions = []
27
+ monthly_budget_limit_usd = 0
22
28
 
23
29
  desired_counts = {
24
30
  web = 0
@@ -27,3 +33,5 @@ desired_counts = {
27
33
  reporter = 0
28
34
  migration = 0
29
35
  }
36
+
37
+ budget_alert_email_addresses = []
@@ -22,6 +22,36 @@ variable "service_name" {
22
22
  default = "open-uptime"
23
23
  }
24
24
 
25
+ variable "project_name" {
26
+ description = "Project tag value for cost allocation."
27
+ type = string
28
+ default = "open-uptime"
29
+ }
30
+
31
+ variable "owner" {
32
+ description = "Owner tag value for cost allocation and operations."
33
+ type = string
34
+ default = "hasna"
35
+ }
36
+
37
+ variable "app_type" {
38
+ description = "AppType tag value."
39
+ type = string
40
+ default = "opensource"
41
+ }
42
+
43
+ variable "environment" {
44
+ description = "Environment tag value."
45
+ type = string
46
+ default = "prod"
47
+ }
48
+
49
+ variable "cost_center" {
50
+ description = "CostCenter tag value."
51
+ type = string
52
+ default = "opensource"
53
+ }
54
+
25
55
  variable "hostname" {
26
56
  description = "Public/internal hostname for Open Uptime."
27
57
  type = string
@@ -86,7 +116,7 @@ variable "container_image" {
86
116
  variable "runtime_package_version" {
87
117
  description = "Published @hasna/uptime package version that CodeBuild should build into the ECR image."
88
118
  type = string
89
- default = "0.1.8"
119
+ default = "0.1.9"
90
120
 
91
121
  validation {
92
122
  condition = can(regex("^[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9A-Za-z.-]+)?$", var.runtime_package_version))
@@ -190,3 +220,20 @@ variable "alarm_actions" {
190
220
  type = list(string)
191
221
  default = []
192
222
  }
223
+
224
+ variable "monthly_budget_limit_usd" {
225
+ description = "Optional monthly AWS Budgets limit in USD. Set with budget_alert_email_addresses to create a budget alert."
226
+ type = number
227
+ default = 0
228
+
229
+ validation {
230
+ condition = var.monthly_budget_limit_usd >= 0
231
+ error_message = "monthly_budget_limit_usd must be non-negative."
232
+ }
233
+ }
234
+
235
+ variable "budget_alert_email_addresses" {
236
+ description = "Email recipients for AWS Budgets forecasted and actual alerts. Leave empty to skip budget creation."
237
+ type = list(string)
238
+ default = []
239
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/uptime",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Local-first uptime and downtime monitoring service with CLI, MCP, SDK, SQLite persistence, and a dashboard.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",