@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 +12 -3
- package/README.md +4 -4
- package/dist/cli/index.js +21 -21
- package/dist/cloud-plan.d.ts +6 -6
- package/dist/cloud-plan.d.ts.map +1 -1
- package/dist/cloud-plan.js +17 -17
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -17
- package/docs/aws-deployment-runbook.md +15 -9
- package/infra/aws/README.md +12 -1
- package/infra/aws/main.tf +44 -5
- package/infra/aws/terraform.tfvars.example +9 -1
- package/infra/aws/variables.tf +48 -1
- package/package.json +1 -1
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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/
|
|
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
|
|
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 ./
|
|
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.
|
|
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
|
-
|
|
6613
|
+
privateProbe: [
|
|
6614
6614
|
"Create a private probe identity with a caller-managed public key.",
|
|
6615
|
-
"Install @hasna/uptime on
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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, "
|
|
6656
|
-
const privateKeyFile = clean(options.probePrivateKeyFile, "~/.hasna/uptime/probes/
|
|
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
|
-
"
|
|
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.
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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(`
|
|
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
|
|
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("
|
|
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 =
|
|
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(
|
|
7092
|
+
console.log(renderPrivateProbeEnv(config));
|
|
7093
7093
|
return;
|
|
7094
7094
|
}
|
|
7095
|
-
print(config,
|
|
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
|
|
7475
|
+
function renderPrivateProbeConfig(config) {
|
|
7476
7476
|
return [
|
|
7477
7477
|
`${config.machineId} ${config.mode} config`,
|
|
7478
7478
|
`status: ${config.status}`,
|
package/dist/cloud-plan.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ export interface AwsDeploymentPlan {
|
|
|
75
75
|
provision: string[];
|
|
76
76
|
deploy: string[];
|
|
77
77
|
rollback: string[];
|
|
78
|
-
|
|
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
|
|
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
|
|
110
|
-
kind: "open-uptime.
|
|
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
|
|
133
|
-
export declare function
|
|
132
|
+
export declare function buildPrivateProbeCloudConfig(options?: PrivateProbeCloudConfigOptions): PrivateProbeCloudConfig;
|
|
133
|
+
export declare function renderPrivateProbeEnv(config: PrivateProbeCloudConfig): string;
|
|
134
134
|
//# sourceMappingURL=cloud-plan.d.ts.map
|
package/dist/cloud-plan.d.ts.map
CHANGED
|
@@ -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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"}
|
package/dist/cloud-plan.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
156
|
+
privateProbe: [
|
|
157
157
|
"Create a private probe identity with a caller-managed public key.",
|
|
158
|
-
"Install @hasna/uptime on
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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, "
|
|
199
|
-
const privateKeyFile = clean(options.probePrivateKeyFile, "~/.hasna/uptime/probes/
|
|
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
|
-
"
|
|
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.
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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(`
|
|
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
|
-
|
|
294
|
-
|
|
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,
|
|
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,
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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.
|
|
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
|
-
|
|
4016
|
+
privateProbe: [
|
|
4017
4017
|
"Create a private probe identity with a caller-managed public key.",
|
|
4018
|
-
"Install @hasna/uptime on
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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, "
|
|
4059
|
-
const privateKeyFile = clean(options.probePrivateKeyFile, "~/.hasna/uptime/probes/
|
|
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
|
-
"
|
|
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.
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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(`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
##
|
|
92
|
+
## Private Probe Operator
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
truth. The generated env file points
|
|
96
|
-
references a local private-key file path. It does not include private
|
|
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
|
|
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
|
|
package/infra/aws/README.md
CHANGED
|
@@ -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
|
-
-
|
|
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
|
|
57
|
-
Service
|
|
58
|
-
|
|
59
|
-
|
|
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 =
|
|
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.
|
|
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 = []
|
package/infra/aws/variables.tf
CHANGED
|
@@ -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.
|
|
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