@hasna/uptime 0.1.5 → 0.1.6
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/.dockerignore +14 -0
- package/CHANGELOG.md +21 -2
- package/Dockerfile +30 -0
- package/README.md +4 -0
- package/dist/cli/index.js +24 -13
- package/dist/cloud-plan.d.ts +10 -0
- package/dist/cloud-plan.d.ts.map +1 -1
- package/dist/cloud-plan.js +20 -11
- package/dist/index.js +20 -11
- package/docs/aws-deployment-runbook.md +19 -4
- package/infra/aws/.terraform.lock.hcl +25 -0
- package/infra/aws/README.md +32 -0
- package/infra/aws/main.tf +546 -0
- package/infra/aws/outputs.tf +22 -0
- package/infra/aws/terraform.tfvars.example +28 -0
- package/infra/aws/variables.tf +166 -0
- package/package.json +7 -1
package/.dockerignore
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,25 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.1.6] - 2026-06-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Bun-based hosted runtime `Dockerfile` and `.dockerignore`.
|
|
14
|
+
- Reviewable Terraform/OpenTofu AWS starter plan under `infra/aws` for ECR,
|
|
15
|
+
S3 evidence storage, ECS/Fargate services, ALB/TLS/DNS, task roles,
|
|
16
|
+
CloudWatch logs, security groups, and secret refs.
|
|
17
|
+
- Cloud plan SDK/CLI fields that point to `Dockerfile` and `infra/aws` with
|
|
18
|
+
format/init/validate/plan commands while keeping apply disabled.
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
|
|
22
|
+
- AWS infra templates use secret ARNs/valueFrom references and example
|
|
23
|
+
placeholders only; no plaintext service tokens, database URLs, or private keys
|
|
24
|
+
are stored in the repo.
|
|
25
|
+
- Terraform desired counts default to zero until hosted cloud-store/auth/probe
|
|
26
|
+
blockers are closed.
|
|
27
|
+
|
|
9
28
|
## [0.1.5] - 2026-06-28
|
|
10
29
|
|
|
11
30
|
### Added
|
|
@@ -13,8 +32,8 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
13
32
|
- Dry-run AWS deployment plan generator for the `hasna-xyz-infra` target,
|
|
14
33
|
covering ECS/Fargate services, ECR image commands, ALB/RDS/S3/Secrets/Logs
|
|
15
34
|
resources, rollback steps, and safety assertions.
|
|
16
|
-
- Spark01
|
|
17
|
-
rendering.
|
|
35
|
+
- Spark01 hosted-targeted private probe preflight config generator with JSON and
|
|
36
|
+
env-file rendering.
|
|
18
37
|
- CLI commands `uptime cloud plan` and `uptime cloud spark01-config`.
|
|
19
38
|
- SDK export `@hasna/uptime/cloud-plan`.
|
|
20
39
|
- Machine-readable `blocked`/`canApply:false` and `blocked`/`canStart:false`
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
|
|
3
|
+
FROM oven/bun:1.3.13-slim AS build
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
COPY package.json bun.lock tsconfig.json tsconfig.build.json ./
|
|
7
|
+
COPY src ./src
|
|
8
|
+
|
|
9
|
+
RUN bun install --frozen-lockfile
|
|
10
|
+
RUN bun run build
|
|
11
|
+
RUN bun install --production --frozen-lockfile
|
|
12
|
+
|
|
13
|
+
FROM oven/bun:1.3.13-slim AS runtime
|
|
14
|
+
ENV NODE_ENV=production \
|
|
15
|
+
HASNA_UPTIME_MODE=hosted
|
|
16
|
+
WORKDIR /app
|
|
17
|
+
|
|
18
|
+
RUN addgroup --system uptime && adduser --system --ingroup uptime uptime
|
|
19
|
+
|
|
20
|
+
COPY --from=build /app/package.json ./package.json
|
|
21
|
+
COPY --from=build /app/node_modules ./node_modules
|
|
22
|
+
COPY --from=build /app/dist ./dist
|
|
23
|
+
|
|
24
|
+
USER uptime
|
|
25
|
+
EXPOSE 3899
|
|
26
|
+
|
|
27
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
28
|
+
CMD bun -e "const r = await fetch('http://127.0.0.1:3899/health'); process.exit(r.ok ? 0 : 1)"
|
|
29
|
+
|
|
30
|
+
CMD ["bun", "dist/cli/index.js", "serve", "--mode", "hosted", "--host", "0.0.0.0", "--port", "3899"]
|
package/README.md
CHANGED
|
@@ -46,6 +46,10 @@ 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.
|
|
48
48
|
|
|
49
|
+
Deployment review artifacts live in `Dockerfile` and `infra/aws`. The Terraform
|
|
50
|
+
desired counts default to zero, and `uptime cloud plan --json` exposes the
|
|
51
|
+
format/init/validate/plan commands with `applyAllowed: false`.
|
|
52
|
+
|
|
49
53
|
Private/local probes can submit signed results from another machine:
|
|
50
54
|
|
|
51
55
|
```bash
|
package/dist/cli/index.js
CHANGED
|
@@ -6404,7 +6404,8 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
6404
6404
|
const hostname = clean(options.hostname, DEFAULT_HOSTNAME);
|
|
6405
6405
|
const workspaceId = clean(options.workspaceId, DEFAULT_WORKSPACE_ID);
|
|
6406
6406
|
const ecrRepository = clean(options.ecrRepository, `hasna/opensource/${prefix}`);
|
|
6407
|
-
const
|
|
6407
|
+
const imageRepositoryUri = `<account-id>.dkr.ecr.${region}.amazonaws.com/${ecrRepository}`;
|
|
6408
|
+
const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
|
|
6408
6409
|
const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
|
|
6409
6410
|
const cluster = `${prefix}-${stage}`;
|
|
6410
6411
|
const secrets = {
|
|
@@ -6470,26 +6471,34 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
6470
6471
|
`${prefix}-${stage}-web-sg`,
|
|
6471
6472
|
`${prefix}-${stage}-scheduler-sg`,
|
|
6472
6473
|
`${prefix}-${stage}-public-probe-sg`,
|
|
6473
|
-
`${prefix}-${stage}-
|
|
6474
|
+
`${prefix}-${stage}-reporter-sg`,
|
|
6475
|
+
`${prefix}-${stage}-migration-sg`
|
|
6474
6476
|
],
|
|
6475
6477
|
secrets,
|
|
6476
6478
|
logGroups: services.map((service) => service.logGroup),
|
|
6477
6479
|
alarms: [
|
|
6478
6480
|
`${prefix}-${stage}-web-5xx`,
|
|
6479
|
-
`${prefix}-${stage}-
|
|
6480
|
-
`${prefix}-${stage}-probe-stale`,
|
|
6481
|
-
`${prefix}-${stage}-report-delivery-failures`
|
|
6481
|
+
`${prefix}-${stage}-web-unhealthy`
|
|
6482
6482
|
]
|
|
6483
6483
|
},
|
|
6484
6484
|
image: {
|
|
6485
6485
|
repository: ecrRepository,
|
|
6486
6486
|
uri: image,
|
|
6487
|
-
|
|
6487
|
+
dockerfile: "Dockerfile",
|
|
6488
|
+
buildCommand: `docker build --pull -t ${imageRepositoryUri}:<git-sha> .`,
|
|
6488
6489
|
pushCommands: [
|
|
6489
6490
|
"BLOCKED: push only from approved CI/CD after the ECR repository and image digest policy exist",
|
|
6490
6491
|
"BLOCKED: deploy services by immutable image digest, not by mutable tags"
|
|
6491
6492
|
]
|
|
6492
6493
|
},
|
|
6494
|
+
infra: {
|
|
6495
|
+
path: "infra/aws",
|
|
6496
|
+
fmtCommand: "terraform -chdir=infra/aws fmt -check",
|
|
6497
|
+
initCommand: "terraform -chdir=infra/aws init -backend=false",
|
|
6498
|
+
validateCommand: "terraform -chdir=infra/aws validate",
|
|
6499
|
+
planCommand: "terraform -chdir=infra/aws plan -out open-uptime.tfplan",
|
|
6500
|
+
applyAllowed: false
|
|
6501
|
+
},
|
|
6493
6502
|
runbook: {
|
|
6494
6503
|
preflight: [
|
|
6495
6504
|
`aws sts get-caller-identity --profile ${accountName}`,
|
|
@@ -6524,7 +6533,6 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
6524
6533
|
},
|
|
6525
6534
|
blockers: [
|
|
6526
6535
|
"The hasna-xyz-infra infrastructure owner repository was not found in this workspace.",
|
|
6527
|
-
"The repo has no reviewed Dockerfile/container build target for image build and publish automation.",
|
|
6528
6536
|
"Hosted Postgres storage adapter and migrations are not implemented.",
|
|
6529
6537
|
"Hosted production auth/RBAC must replace broad static hosted-token operation before exposure.",
|
|
6530
6538
|
"Public probe execution still needs DNS, redirect, and rebinding SSRF enforcement plus cloud check-job leases.",
|
|
@@ -6534,7 +6542,7 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
6534
6542
|
"Infrastructure PR/synth/plan from the approved infra repository.",
|
|
6535
6543
|
"Container build smoke and immutable image digest.",
|
|
6536
6544
|
"ECS task definitions using secrets.valueFrom only.",
|
|
6537
|
-
"ALB/TLS/DNS/auth denial smokes.",
|
|
6545
|
+
"ALB/TLS/DNS/auth denial smokes and web alarm checks.",
|
|
6538
6546
|
"RDS TLS, backups/PITR, scoped roles, and migration dry-run evidence.",
|
|
6539
6547
|
"S3 bucket KMS, versioning, lifecycle, and public-access-block evidence.",
|
|
6540
6548
|
"Spark01 private-probe registration, key-file mode, heartbeat, and revocation evidence."
|
|
@@ -6606,7 +6614,7 @@ function buildSpark01CloudConfig(options = {}) {
|
|
|
6606
6614
|
privateKeyInline: false,
|
|
6607
6615
|
tokenInline: false,
|
|
6608
6616
|
notes: [
|
|
6609
|
-
"This config is
|
|
6617
|
+
"This config is hosted-targeted preflight: Spark01 must not start until cloud probe routes are backed by hosted state.",
|
|
6610
6618
|
"The private key file path is referenced, not embedded.",
|
|
6611
6619
|
"Hosted token or probe auth material must come from the machine secret store, not this generated config."
|
|
6612
6620
|
]
|
|
@@ -6627,7 +6635,8 @@ function servicePlan(prefix, stage, role, desiredCount, image, workspaceId, secr
|
|
|
6627
6635
|
return {
|
|
6628
6636
|
name,
|
|
6629
6637
|
role,
|
|
6630
|
-
desiredCount,
|
|
6638
|
+
desiredCount: 0,
|
|
6639
|
+
targetDesiredCount: desiredCount,
|
|
6631
6640
|
taskRole: `${name}-task-role`,
|
|
6632
6641
|
executionRole: `${prefix}-${stage}-execution-role`,
|
|
6633
6642
|
logGroup: `/ecs/${name}`,
|
|
@@ -6636,7 +6645,7 @@ function servicePlan(prefix, stage, role, desiredCount, image, workspaceId, secr
|
|
|
6636
6645
|
HASNA_UPTIME_IMAGE: image,
|
|
6637
6646
|
...environment
|
|
6638
6647
|
},
|
|
6639
|
-
secrets: role === "
|
|
6648
|
+
secrets: role === "web" ? { HASNA_UPTIME_DATABASE_URL: secrets.database, APP_ENV: secrets.appEnv, HASNA_UPTIME_HOSTED_TOKEN: secrets.hostedToken } : role === "public-probe" ? { PROBE_CONFIG: secrets.publicProbe } : role === "reporter" ? { HASNA_UPTIME_DATABASE_URL: secrets.database, REPORTING_CONFIG: secrets.reporting } : { HASNA_UPTIME_DATABASE_URL: secrets.database, APP_ENV: secrets.appEnv }
|
|
6640
6649
|
};
|
|
6641
6650
|
}
|
|
6642
6651
|
function clean(value, fallback) {
|
|
@@ -6976,7 +6985,7 @@ cloud.command("plan").description("Generate a dry-run AWS deployment plan for ha
|
|
|
6976
6985
|
fail(error);
|
|
6977
6986
|
}
|
|
6978
6987
|
});
|
|
6979
|
-
cloud.command("spark01-config").description("Generate Spark01
|
|
6988
|
+
cloud.command("spark01-config").description("Generate Spark01 hosted-targeted private probe preflight configuration").option("--api-url <url>", "hosted Open Uptime API URL", "https://uptime.hasna.xyz/api/v1").option("--workspace-id <id>", "workspace id", "wks_2tyysw05cwap").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) => {
|
|
6980
6989
|
try {
|
|
6981
6990
|
const config = buildSpark01CloudConfig({
|
|
6982
6991
|
apiUrl: opts.apiUrl,
|
|
@@ -7355,9 +7364,11 @@ function renderCloudPlan(plan) {
|
|
|
7355
7364
|
`host: ${plan.hostname}`,
|
|
7356
7365
|
`cluster: ${plan.resources.ecsCluster}`,
|
|
7357
7366
|
`image: ${plan.image.uri}`,
|
|
7367
|
+
`dockerfile: ${plan.image.dockerfile}`,
|
|
7368
|
+
`infra: ${plan.infra.path}`,
|
|
7358
7369
|
`vpc: ${plan.resources.vpcId}`,
|
|
7359
7370
|
`rds: ${plan.resources.rdsInstanceId}`,
|
|
7360
|
-
`services: ${plan.resources.services.map((service2) => `${service2.name}:${service2.desiredCount}`).join(", ")}`,
|
|
7371
|
+
`services: ${plan.resources.services.map((service2) => `${service2.name}:${service2.desiredCount}/${service2.targetDesiredCount}`).join(", ")}`,
|
|
7361
7372
|
`evidence bucket: ${plan.resources.evidenceBucket}`,
|
|
7362
7373
|
`blockers: ${plan.blockers.length}`,
|
|
7363
7374
|
"live AWS mutation: false"
|
package/dist/cloud-plan.d.ts
CHANGED
|
@@ -47,9 +47,18 @@ export interface AwsDeploymentPlan {
|
|
|
47
47
|
image: {
|
|
48
48
|
repository: string;
|
|
49
49
|
uri: string;
|
|
50
|
+
dockerfile: string;
|
|
50
51
|
buildCommand: string;
|
|
51
52
|
pushCommands: string[];
|
|
52
53
|
};
|
|
54
|
+
infra: {
|
|
55
|
+
path: string;
|
|
56
|
+
fmtCommand: string;
|
|
57
|
+
initCommand: string;
|
|
58
|
+
validateCommand: string;
|
|
59
|
+
planCommand: string;
|
|
60
|
+
applyAllowed: false;
|
|
61
|
+
};
|
|
53
62
|
runbook: {
|
|
54
63
|
preflight: string[];
|
|
55
64
|
provision: string[];
|
|
@@ -70,6 +79,7 @@ export interface AwsServicePlan {
|
|
|
70
79
|
name: string;
|
|
71
80
|
role: "web" | "scheduler" | "public-probe" | "reporter" | "migration";
|
|
72
81
|
desiredCount: number;
|
|
82
|
+
targetDesiredCount: number;
|
|
73
83
|
taskRole: string;
|
|
74
84
|
executionRole: string;
|
|
75
85
|
logGroup: string;
|
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
|
@@ -16,7 +16,8 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
16
16
|
const hostname = clean(options.hostname, DEFAULT_HOSTNAME);
|
|
17
17
|
const workspaceId = clean(options.workspaceId, DEFAULT_WORKSPACE_ID);
|
|
18
18
|
const ecrRepository = clean(options.ecrRepository, `hasna/opensource/${prefix}`);
|
|
19
|
-
const
|
|
19
|
+
const imageRepositoryUri = `<account-id>.dkr.ecr.${region}.amazonaws.com/${ecrRepository}`;
|
|
20
|
+
const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
|
|
20
21
|
const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
|
|
21
22
|
const cluster = `${prefix}-${stage}`;
|
|
22
23
|
const secrets = {
|
|
@@ -82,26 +83,34 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
82
83
|
`${prefix}-${stage}-web-sg`,
|
|
83
84
|
`${prefix}-${stage}-scheduler-sg`,
|
|
84
85
|
`${prefix}-${stage}-public-probe-sg`,
|
|
85
|
-
`${prefix}-${stage}-
|
|
86
|
+
`${prefix}-${stage}-reporter-sg`,
|
|
87
|
+
`${prefix}-${stage}-migration-sg`
|
|
86
88
|
],
|
|
87
89
|
secrets,
|
|
88
90
|
logGroups: services.map((service) => service.logGroup),
|
|
89
91
|
alarms: [
|
|
90
92
|
`${prefix}-${stage}-web-5xx`,
|
|
91
|
-
`${prefix}-${stage}-
|
|
92
|
-
`${prefix}-${stage}-probe-stale`,
|
|
93
|
-
`${prefix}-${stage}-report-delivery-failures`
|
|
93
|
+
`${prefix}-${stage}-web-unhealthy`
|
|
94
94
|
]
|
|
95
95
|
},
|
|
96
96
|
image: {
|
|
97
97
|
repository: ecrRepository,
|
|
98
98
|
uri: image,
|
|
99
|
-
|
|
99
|
+
dockerfile: "Dockerfile",
|
|
100
|
+
buildCommand: `docker build --pull -t ${imageRepositoryUri}:<git-sha> .`,
|
|
100
101
|
pushCommands: [
|
|
101
102
|
"BLOCKED: push only from approved CI/CD after the ECR repository and image digest policy exist",
|
|
102
103
|
"BLOCKED: deploy services by immutable image digest, not by mutable tags"
|
|
103
104
|
]
|
|
104
105
|
},
|
|
106
|
+
infra: {
|
|
107
|
+
path: "infra/aws",
|
|
108
|
+
fmtCommand: "terraform -chdir=infra/aws fmt -check",
|
|
109
|
+
initCommand: "terraform -chdir=infra/aws init -backend=false",
|
|
110
|
+
validateCommand: "terraform -chdir=infra/aws validate",
|
|
111
|
+
planCommand: "terraform -chdir=infra/aws plan -out open-uptime.tfplan",
|
|
112
|
+
applyAllowed: false
|
|
113
|
+
},
|
|
105
114
|
runbook: {
|
|
106
115
|
preflight: [
|
|
107
116
|
`aws sts get-caller-identity --profile ${accountName}`,
|
|
@@ -136,7 +145,6 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
136
145
|
},
|
|
137
146
|
blockers: [
|
|
138
147
|
"The hasna-xyz-infra infrastructure owner repository was not found in this workspace.",
|
|
139
|
-
"The repo has no reviewed Dockerfile/container build target for image build and publish automation.",
|
|
140
148
|
"Hosted Postgres storage adapter and migrations are not implemented.",
|
|
141
149
|
"Hosted production auth/RBAC must replace broad static hosted-token operation before exposure.",
|
|
142
150
|
"Public probe execution still needs DNS, redirect, and rebinding SSRF enforcement plus cloud check-job leases.",
|
|
@@ -146,7 +154,7 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
146
154
|
"Infrastructure PR/synth/plan from the approved infra repository.",
|
|
147
155
|
"Container build smoke and immutable image digest.",
|
|
148
156
|
"ECS task definitions using secrets.valueFrom only.",
|
|
149
|
-
"ALB/TLS/DNS/auth denial smokes.",
|
|
157
|
+
"ALB/TLS/DNS/auth denial smokes and web alarm checks.",
|
|
150
158
|
"RDS TLS, backups/PITR, scoped roles, and migration dry-run evidence.",
|
|
151
159
|
"S3 bucket KMS, versioning, lifecycle, and public-access-block evidence.",
|
|
152
160
|
"Spark01 private-probe registration, key-file mode, heartbeat, and revocation evidence."
|
|
@@ -218,7 +226,7 @@ function buildSpark01CloudConfig(options = {}) {
|
|
|
218
226
|
privateKeyInline: false,
|
|
219
227
|
tokenInline: false,
|
|
220
228
|
notes: [
|
|
221
|
-
"This config is
|
|
229
|
+
"This config is hosted-targeted preflight: Spark01 must not start until cloud probe routes are backed by hosted state.",
|
|
222
230
|
"The private key file path is referenced, not embedded.",
|
|
223
231
|
"Hosted token or probe auth material must come from the machine secret store, not this generated config."
|
|
224
232
|
]
|
|
@@ -239,7 +247,8 @@ function servicePlan(prefix, stage, role, desiredCount, image, workspaceId, secr
|
|
|
239
247
|
return {
|
|
240
248
|
name,
|
|
241
249
|
role,
|
|
242
|
-
desiredCount,
|
|
250
|
+
desiredCount: 0,
|
|
251
|
+
targetDesiredCount: desiredCount,
|
|
243
252
|
taskRole: `${name}-task-role`,
|
|
244
253
|
executionRole: `${prefix}-${stage}-execution-role`,
|
|
245
254
|
logGroup: `/ecs/${name}`,
|
|
@@ -248,7 +257,7 @@ function servicePlan(prefix, stage, role, desiredCount, image, workspaceId, secr
|
|
|
248
257
|
HASNA_UPTIME_IMAGE: image,
|
|
249
258
|
...environment
|
|
250
259
|
},
|
|
251
|
-
secrets: role === "
|
|
260
|
+
secrets: role === "web" ? { HASNA_UPTIME_DATABASE_URL: secrets.database, APP_ENV: secrets.appEnv, HASNA_UPTIME_HOSTED_TOKEN: secrets.hostedToken } : role === "public-probe" ? { PROBE_CONFIG: secrets.publicProbe } : role === "reporter" ? { HASNA_UPTIME_DATABASE_URL: secrets.database, REPORTING_CONFIG: secrets.reporting } : { HASNA_UPTIME_DATABASE_URL: secrets.database, APP_ENV: secrets.appEnv }
|
|
252
261
|
};
|
|
253
262
|
}
|
|
254
263
|
function clean(value, fallback) {
|
package/dist/index.js
CHANGED
|
@@ -3807,7 +3807,8 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
3807
3807
|
const hostname = clean(options.hostname, DEFAULT_HOSTNAME);
|
|
3808
3808
|
const workspaceId = clean(options.workspaceId, DEFAULT_WORKSPACE_ID);
|
|
3809
3809
|
const ecrRepository = clean(options.ecrRepository, `hasna/opensource/${prefix}`);
|
|
3810
|
-
const
|
|
3810
|
+
const imageRepositoryUri = `<account-id>.dkr.ecr.${region}.amazonaws.com/${ecrRepository}`;
|
|
3811
|
+
const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
|
|
3811
3812
|
const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
|
|
3812
3813
|
const cluster = `${prefix}-${stage}`;
|
|
3813
3814
|
const secrets = {
|
|
@@ -3873,26 +3874,34 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
3873
3874
|
`${prefix}-${stage}-web-sg`,
|
|
3874
3875
|
`${prefix}-${stage}-scheduler-sg`,
|
|
3875
3876
|
`${prefix}-${stage}-public-probe-sg`,
|
|
3876
|
-
`${prefix}-${stage}-
|
|
3877
|
+
`${prefix}-${stage}-reporter-sg`,
|
|
3878
|
+
`${prefix}-${stage}-migration-sg`
|
|
3877
3879
|
],
|
|
3878
3880
|
secrets,
|
|
3879
3881
|
logGroups: services.map((service) => service.logGroup),
|
|
3880
3882
|
alarms: [
|
|
3881
3883
|
`${prefix}-${stage}-web-5xx`,
|
|
3882
|
-
`${prefix}-${stage}-
|
|
3883
|
-
`${prefix}-${stage}-probe-stale`,
|
|
3884
|
-
`${prefix}-${stage}-report-delivery-failures`
|
|
3884
|
+
`${prefix}-${stage}-web-unhealthy`
|
|
3885
3885
|
]
|
|
3886
3886
|
},
|
|
3887
3887
|
image: {
|
|
3888
3888
|
repository: ecrRepository,
|
|
3889
3889
|
uri: image,
|
|
3890
|
-
|
|
3890
|
+
dockerfile: "Dockerfile",
|
|
3891
|
+
buildCommand: `docker build --pull -t ${imageRepositoryUri}:<git-sha> .`,
|
|
3891
3892
|
pushCommands: [
|
|
3892
3893
|
"BLOCKED: push only from approved CI/CD after the ECR repository and image digest policy exist",
|
|
3893
3894
|
"BLOCKED: deploy services by immutable image digest, not by mutable tags"
|
|
3894
3895
|
]
|
|
3895
3896
|
},
|
|
3897
|
+
infra: {
|
|
3898
|
+
path: "infra/aws",
|
|
3899
|
+
fmtCommand: "terraform -chdir=infra/aws fmt -check",
|
|
3900
|
+
initCommand: "terraform -chdir=infra/aws init -backend=false",
|
|
3901
|
+
validateCommand: "terraform -chdir=infra/aws validate",
|
|
3902
|
+
planCommand: "terraform -chdir=infra/aws plan -out open-uptime.tfplan",
|
|
3903
|
+
applyAllowed: false
|
|
3904
|
+
},
|
|
3896
3905
|
runbook: {
|
|
3897
3906
|
preflight: [
|
|
3898
3907
|
`aws sts get-caller-identity --profile ${accountName}`,
|
|
@@ -3927,7 +3936,6 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
3927
3936
|
},
|
|
3928
3937
|
blockers: [
|
|
3929
3938
|
"The hasna-xyz-infra infrastructure owner repository was not found in this workspace.",
|
|
3930
|
-
"The repo has no reviewed Dockerfile/container build target for image build and publish automation.",
|
|
3931
3939
|
"Hosted Postgres storage adapter and migrations are not implemented.",
|
|
3932
3940
|
"Hosted production auth/RBAC must replace broad static hosted-token operation before exposure.",
|
|
3933
3941
|
"Public probe execution still needs DNS, redirect, and rebinding SSRF enforcement plus cloud check-job leases.",
|
|
@@ -3937,7 +3945,7 @@ function buildAwsDeploymentPlan(options = {}) {
|
|
|
3937
3945
|
"Infrastructure PR/synth/plan from the approved infra repository.",
|
|
3938
3946
|
"Container build smoke and immutable image digest.",
|
|
3939
3947
|
"ECS task definitions using secrets.valueFrom only.",
|
|
3940
|
-
"ALB/TLS/DNS/auth denial smokes.",
|
|
3948
|
+
"ALB/TLS/DNS/auth denial smokes and web alarm checks.",
|
|
3941
3949
|
"RDS TLS, backups/PITR, scoped roles, and migration dry-run evidence.",
|
|
3942
3950
|
"S3 bucket KMS, versioning, lifecycle, and public-access-block evidence.",
|
|
3943
3951
|
"Spark01 private-probe registration, key-file mode, heartbeat, and revocation evidence."
|
|
@@ -4009,7 +4017,7 @@ function buildSpark01CloudConfig(options = {}) {
|
|
|
4009
4017
|
privateKeyInline: false,
|
|
4010
4018
|
tokenInline: false,
|
|
4011
4019
|
notes: [
|
|
4012
|
-
"This config is
|
|
4020
|
+
"This config is hosted-targeted preflight: Spark01 must not start until cloud probe routes are backed by hosted state.",
|
|
4013
4021
|
"The private key file path is referenced, not embedded.",
|
|
4014
4022
|
"Hosted token or probe auth material must come from the machine secret store, not this generated config."
|
|
4015
4023
|
]
|
|
@@ -4030,7 +4038,8 @@ function servicePlan(prefix, stage, role, desiredCount, image, workspaceId, secr
|
|
|
4030
4038
|
return {
|
|
4031
4039
|
name,
|
|
4032
4040
|
role,
|
|
4033
|
-
desiredCount,
|
|
4041
|
+
desiredCount: 0,
|
|
4042
|
+
targetDesiredCount: desiredCount,
|
|
4034
4043
|
taskRole: `${name}-task-role`,
|
|
4035
4044
|
executionRole: `${prefix}-${stage}-execution-role`,
|
|
4036
4045
|
logGroup: `/ecs/${name}`,
|
|
@@ -4039,7 +4048,7 @@ function servicePlan(prefix, stage, role, desiredCount, image, workspaceId, secr
|
|
|
4039
4048
|
HASNA_UPTIME_IMAGE: image,
|
|
4040
4049
|
...environment
|
|
4041
4050
|
},
|
|
4042
|
-
secrets: role === "
|
|
4051
|
+
secrets: role === "web" ? { HASNA_UPTIME_DATABASE_URL: secrets.database, APP_ENV: secrets.appEnv, HASNA_UPTIME_HOSTED_TOKEN: secrets.hostedToken } : role === "public-probe" ? { PROBE_CONFIG: secrets.publicProbe } : role === "reporter" ? { HASNA_UPTIME_DATABASE_URL: secrets.database, REPORTING_CONFIG: secrets.reporting } : { HASNA_UPTIME_DATABASE_URL: secrets.database, APP_ENV: secrets.appEnv }
|
|
4043
4052
|
};
|
|
4044
4053
|
}
|
|
4045
4054
|
function clean(value, fallback) {
|
|
@@ -27,6 +27,10 @@ The generated AWS plan currently returns `status: "blocked"` and
|
|
|
27
27
|
`canStart: false`. Treat both as review/preflight artifacts until the blockers
|
|
28
28
|
and required evidence in the JSON output are resolved.
|
|
29
29
|
|
|
30
|
+
The app repo includes a hosted runtime `Dockerfile` and Terraform/OpenTofu
|
|
31
|
+
starter files in `infra/aws`. The plan output points to these files and keeps
|
|
32
|
+
`applyAllowed: false`.
|
|
33
|
+
|
|
30
34
|
`uptime cloud spark01-config --env` requires a real `--probe-id`; it will not
|
|
31
35
|
write a sourceable env file with a placeholder probe identity.
|
|
32
36
|
|
|
@@ -57,13 +61,23 @@ The plan expects:
|
|
|
57
61
|
- S3 bucket for redacted browser evidence and generated report artifacts.
|
|
58
62
|
- Secrets Manager or SSM refs for database, app env, probe config, and
|
|
59
63
|
reporting channel refs.
|
|
60
|
-
- CloudWatch log groups
|
|
61
|
-
and report
|
|
64
|
+
- CloudWatch log groups for every component plus initial web 5xx/unhealthy
|
|
65
|
+
alarms. Scheduler-stall, stale-probe, and report-delivery alarms remain
|
|
66
|
+
blocked until those workers emit cloud metrics.
|
|
62
67
|
|
|
63
68
|
Provision these through the approved infrastructure repository and reviewed
|
|
64
69
|
plan/apply flow. The local `uptime cloud plan` output intentionally avoids
|
|
65
70
|
copy-pastable AWS mutation commands.
|
|
66
71
|
|
|
72
|
+
Plan the included Terraform/OpenTofu starter without a backend:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
terraform -chdir=infra/aws fmt -check
|
|
76
|
+
terraform -chdir=infra/aws init -backend=false
|
|
77
|
+
terraform -chdir=infra/aws validate
|
|
78
|
+
terraform -chdir=infra/aws plan -out open-uptime.tfplan
|
|
79
|
+
```
|
|
80
|
+
|
|
67
81
|
## Spark01
|
|
68
82
|
|
|
69
83
|
Spark01 should be a private probe/operator machine, not the hosted source of
|
|
@@ -77,8 +91,9 @@ routes are backed by cloud check jobs and cloud audit rows.
|
|
|
77
91
|
## Safety Rules
|
|
78
92
|
|
|
79
93
|
- Do not deploy hosted mode with `HASNA_UPTIME_ALLOW_HOSTED_LOCAL_STORE=1`.
|
|
80
|
-
- Do not inline AWS keys, hosted tokens, Mailery keys, Open Logs tokens,
|
|
81
|
-
probe private keys in task definitions.
|
|
94
|
+
- Do not inline AWS keys, hosted tokens, Mailery keys, Open Logs tokens, database
|
|
95
|
+
URLs, or probe private keys in task definitions. Use ECS `secrets.valueFrom`
|
|
96
|
+
refs such as `HASNA_UPTIME_DATABASE_URL` and `HASNA_UPTIME_HOSTED_TOKEN`.
|
|
82
97
|
- Do not run public probe workers against private targets.
|
|
83
98
|
- Do not expose dashboard/API routes without hosted auth and workspace checks.
|
|
84
99
|
- Do not treat local SQLite, local project DBs, or Spark01 local state as cloud
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# This file is maintained automatically by "terraform init".
|
|
2
|
+
# Manual edits may be lost in future updates.
|
|
3
|
+
|
|
4
|
+
provider "registry.terraform.io/hashicorp/aws" {
|
|
5
|
+
version = "5.100.0"
|
|
6
|
+
constraints = "~> 5.0"
|
|
7
|
+
hashes = [
|
|
8
|
+
"h1:wOhTPz6apLBuF7/FYZuCoXRK/MLgrNprZ3vXmq83g5k=",
|
|
9
|
+
"zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644",
|
|
10
|
+
"zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2",
|
|
11
|
+
"zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274",
|
|
12
|
+
"zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b",
|
|
13
|
+
"zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862",
|
|
14
|
+
"zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342",
|
|
15
|
+
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
|
|
16
|
+
"zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93",
|
|
17
|
+
"zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2",
|
|
18
|
+
"zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e",
|
|
19
|
+
"zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421",
|
|
20
|
+
"zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4",
|
|
21
|
+
"zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9",
|
|
22
|
+
"zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9",
|
|
23
|
+
"zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70",
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Open Uptime AWS Infra
|
|
2
|
+
|
|
3
|
+
This directory is a reviewable Terraform/OpenTofu starting point for deploying
|
|
4
|
+
Open Uptime in the `hasna-xyz-infra` AWS account. It is intentionally
|
|
5
|
+
plan-first. Do not apply it directly from this app repo unless the infrastructure
|
|
6
|
+
owner has approved this directory as the source of truth or has copied it into
|
|
7
|
+
the approved infra repository.
|
|
8
|
+
|
|
9
|
+
## Expected Flow
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
terraform -chdir=infra/aws fmt -check
|
|
13
|
+
terraform -chdir=infra/aws init -backend=false
|
|
14
|
+
terraform -chdir=infra/aws validate
|
|
15
|
+
terraform -chdir=infra/aws plan -out open-uptime.tfplan
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Required inputs are declared in `variables.tf` and illustrated in
|
|
19
|
+
`terraform.tfvars.example`. Secrets are passed as Secrets Manager/SSM ARNs only;
|
|
20
|
+
never place plaintext tokens, database URLs, private keys, or channel
|
|
21
|
+
credentials in `.tfvars` files.
|
|
22
|
+
|
|
23
|
+
## Current Blockers
|
|
24
|
+
|
|
25
|
+
- Hosted Postgres adapter and migrations are not implemented in the app yet.
|
|
26
|
+
- Hosted production auth/RBAC still needs scoped, revocable credentials.
|
|
27
|
+
- Public probe runtime still needs execution-time DNS/redirect/rebinding SSRF
|
|
28
|
+
enforcement.
|
|
29
|
+
- Spark01 hosted private-probe enrollment/heartbeat/revocation is still
|
|
30
|
+
fail-closed.
|
|
31
|
+
|
|
32
|
+
Keep `desired_count` at `0` or plan-only until those blockers are closed.
|