@hasna/uptime 0.1.6 → 0.1.7
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 +0 -1
- package/CHANGELOG.md +20 -1
- package/Dockerfile +2 -1
- package/Dockerfile.package +22 -0
- package/README.md +7 -1
- package/dist/api.js +38 -8
- package/dist/cli/index.js +91 -43
- package/dist/cloud-plan.d.ts +11 -4
- package/dist/cloud-plan.d.ts.map +1 -1
- package/dist/cloud-plan.js +43 -31
- package/dist/index.js +81 -39
- package/dist/mcp/index.js +38 -8
- package/dist/service.d.ts +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +38 -8
- package/dist/store.d.ts +3 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +40 -9
- package/docs/aws-deployment-runbook.md +30 -20
- package/infra/aws/README.md +17 -6
- package/infra/aws/main.tf +282 -33
- package/infra/aws/outputs.tf +12 -0
- package/infra/aws/terraform.tfvars.example +10 -10
- package/infra/aws/variables.tf +25 -21
- package/package.json +2 -1
package/dist/service.js
CHANGED
|
@@ -820,10 +820,12 @@ function ensureUptimeHome() {
|
|
|
820
820
|
}
|
|
821
821
|
|
|
822
822
|
// src/store.ts
|
|
823
|
-
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, statSync } from "fs";
|
|
823
|
+
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, statfsSync, statSync } from "fs";
|
|
824
824
|
import { dirname, join as join2 } from "path";
|
|
825
825
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
826
826
|
import { Database } from "bun:sqlite";
|
|
827
|
+
var DEFAULT_HOSTED_SQLITE_DB_PATH = "/data/uptime/uptime.db";
|
|
828
|
+
var NFS_SUPER_MAGIC = 26985;
|
|
827
829
|
var SECRET_URL_PARAM_PATTERN = /(token|secret|password|passwd|api[_-]?key|access[_-]?token|auth|credential|session)/i;
|
|
828
830
|
var REQUIRED_TABLES = [
|
|
829
831
|
"schema_migrations",
|
|
@@ -860,18 +862,39 @@ class UptimeStore {
|
|
|
860
862
|
this.mode = resolveRuntimeMode(options.mode ?? "local");
|
|
861
863
|
const cloudDatabaseUrl = options.cloudDatabaseUrl ?? process.env.HASNA_UPTIME_DATABASE_URL;
|
|
862
864
|
if (this.mode === "hosted" && cloudDatabaseUrl) {
|
|
863
|
-
throw new Error("hosted
|
|
865
|
+
throw new Error("hosted Postgres adapter is not implemented yet; use HASNA_UPTIME_HOSTED_SQLITE_DB on cloud-mounted storage for the current hosted deployment path");
|
|
864
866
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
+
const hostedSqliteDbPath = options.hostedSqliteDbPath ?? process.env.HASNA_UPTIME_HOSTED_SQLITE_DB;
|
|
868
|
+
if (this.mode === "hosted" && hostedSqliteDbPath) {
|
|
869
|
+
if (hostedSqliteDbPath === ":memory:" || !hostedSqliteDbPath.startsWith("/")) {
|
|
870
|
+
throw new Error("HASNA_UPTIME_HOSTED_SQLITE_DB must be an absolute path on mounted cloud storage");
|
|
871
|
+
}
|
|
872
|
+
const approvedHostedPath = hostedSqliteDbPath === DEFAULT_HOSTED_SQLITE_DB_PATH;
|
|
873
|
+
if (!approvedHostedPath && !allowHostedLocalStore(options.allowHostedLocalStore)) {
|
|
874
|
+
throw new Error(`HASNA_UPTIME_HOSTED_SQLITE_DB must be ${DEFAULT_HOSTED_SQLITE_DB_PATH}; set HASNA_UPTIME_ALLOW_HOSTED_LOCAL_STORE=1 only for explicit local fallback testing`);
|
|
875
|
+
}
|
|
876
|
+
const verifiedCloudMount = approvedHostedPath && isNfsMount(dirname(hostedSqliteDbPath));
|
|
877
|
+
if (approvedHostedPath && !verifiedCloudMount && !allowHostedLocalStore(options.allowHostedLocalStore)) {
|
|
878
|
+
throw new Error(`${DEFAULT_HOSTED_SQLITE_DB_PATH} must be on a mounted EFS/NFS filesystem; refusing to create hosted task-local SQLite`);
|
|
879
|
+
}
|
|
880
|
+
this.dataMode = verifiedCloudMount ? "hosted-efs-sqlite" : "hosted-local-sqlite";
|
|
881
|
+
this.dbPath = hostedSqliteDbPath;
|
|
882
|
+
} else if (this.mode === "hosted") {
|
|
883
|
+
if (!allowHostedLocalStore(options.allowHostedLocalStore)) {
|
|
884
|
+
throw new Error("hosted mode requires HASNA_UPTIME_HOSTED_SQLITE_DB on mounted cloud storage; set HASNA_UPTIME_ALLOW_HOSTED_LOCAL_STORE=1 only for explicit local fallback testing");
|
|
885
|
+
}
|
|
886
|
+
this.dataMode = "hosted-local-sqlite";
|
|
887
|
+
this.dbPath = options.dbPath ?? uptimeHostedFallbackDbPath();
|
|
888
|
+
} else {
|
|
889
|
+
this.dataMode = "local-sqlite";
|
|
890
|
+
this.dbPath = options.dbPath ?? uptimeDbPath();
|
|
867
891
|
}
|
|
868
|
-
|
|
869
|
-
this.dbPath = options.dbPath ?? (this.mode === "hosted" ? uptimeHostedFallbackDbPath() : uptimeDbPath());
|
|
870
|
-
if (this.dbPath !== ":memory:") {
|
|
892
|
+
if (this.dbPath !== ":memory:" && this.dataMode !== "hosted-efs-sqlite") {
|
|
871
893
|
mkdirSync2(dirname(this.dbPath), { recursive: true });
|
|
872
894
|
}
|
|
873
895
|
this.db = new Database(this.dbPath, { create: true });
|
|
874
|
-
this.db.run("PRAGMA journal_mode = WAL");
|
|
896
|
+
this.db.run(this.dataMode === "hosted-efs-sqlite" ? "PRAGMA journal_mode = DELETE" : "PRAGMA journal_mode = WAL");
|
|
897
|
+
this.db.run("PRAGMA busy_timeout = 5000");
|
|
875
898
|
this.db.run("PRAGMA foreign_keys = ON");
|
|
876
899
|
this.migrate();
|
|
877
900
|
}
|
|
@@ -1751,6 +1774,13 @@ function resolveRuntimeMode(mode) {
|
|
|
1751
1774
|
function allowHostedLocalStore(value) {
|
|
1752
1775
|
return value === true || process.env.HASNA_UPTIME_ALLOW_HOSTED_LOCAL_STORE === "1";
|
|
1753
1776
|
}
|
|
1777
|
+
function isNfsMount(path) {
|
|
1778
|
+
try {
|
|
1779
|
+
return statfsSync(path).type === NFS_SUPER_MAGIC;
|
|
1780
|
+
} catch {
|
|
1781
|
+
return false;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1754
1784
|
function verifyBackupFile(backupPath) {
|
|
1755
1785
|
const db = new Database(backupPath, { readonly: true });
|
|
1756
1786
|
try {
|
package/dist/store.d.ts
CHANGED
|
@@ -4,8 +4,10 @@ export interface UptimeStoreOptions {
|
|
|
4
4
|
mode?: UptimeRuntimeMode;
|
|
5
5
|
allowHostedLocalStore?: boolean;
|
|
6
6
|
cloudDatabaseUrl?: string;
|
|
7
|
+
hostedSqliteDbPath?: string;
|
|
7
8
|
}
|
|
8
9
|
export type UptimeRuntimeMode = "local" | "hosted";
|
|
10
|
+
export declare const DEFAULT_HOSTED_SQLITE_DB_PATH = "/data/uptime/uptime.db";
|
|
9
11
|
export interface UptimeBackup {
|
|
10
12
|
sourcePath: string;
|
|
11
13
|
backupPath: string;
|
|
@@ -56,7 +58,7 @@ export declare class StaleCheckResultError extends Error {
|
|
|
56
58
|
export declare class UptimeStore {
|
|
57
59
|
readonly dbPath: string;
|
|
58
60
|
readonly mode: UptimeRuntimeMode;
|
|
59
|
-
readonly dataMode: "local-sqlite" | "hosted-local-sqlite";
|
|
61
|
+
readonly dataMode: "local-sqlite" | "hosted-local-sqlite" | "hosted-efs-sqlite";
|
|
60
62
|
private readonly db;
|
|
61
63
|
constructor(options?: UptimeStoreOptions);
|
|
62
64
|
close(): void;
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,UAAU,EAEV,WAAW,EACX,yBAAyB,EACzB,QAAQ,EACR,oBAAoB,EACpB,0BAA0B,EAC1B,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,OAAO,EAGP,aAAa,EAEb,aAAa,EACb,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,SAAS,EACT,eAAe,EACf,cAAc,EAEd,yBAAyB,EACzB,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/store.js
CHANGED
|
@@ -19,7 +19,7 @@ function ensureUptimeHome() {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// src/store.ts
|
|
22
|
-
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, statSync } from "fs";
|
|
22
|
+
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, statfsSync, statSync } from "fs";
|
|
23
23
|
import { dirname, join as join2 } from "path";
|
|
24
24
|
import { randomUUID } from "crypto";
|
|
25
25
|
import { Database } from "bun:sqlite";
|
|
@@ -107,6 +107,8 @@ var MAX_RETRY_COUNT = 10;
|
|
|
107
107
|
var MAX_RESULT_LIMIT = 1000;
|
|
108
108
|
|
|
109
109
|
// src/store.ts
|
|
110
|
+
var DEFAULT_HOSTED_SQLITE_DB_PATH = "/data/uptime/uptime.db";
|
|
111
|
+
var NFS_SUPER_MAGIC = 26985;
|
|
110
112
|
var SECRET_URL_PARAM_PATTERN = /(token|secret|password|passwd|api[_-]?key|access[_-]?token|auth|credential|session)/i;
|
|
111
113
|
var REQUIRED_TABLES = [
|
|
112
114
|
"schema_migrations",
|
|
@@ -143,18 +145,39 @@ class UptimeStore {
|
|
|
143
145
|
this.mode = resolveRuntimeMode(options.mode ?? "local");
|
|
144
146
|
const cloudDatabaseUrl = options.cloudDatabaseUrl ?? process.env.HASNA_UPTIME_DATABASE_URL;
|
|
145
147
|
if (this.mode === "hosted" && cloudDatabaseUrl) {
|
|
146
|
-
throw new Error("hosted
|
|
148
|
+
throw new Error("hosted Postgres adapter is not implemented yet; use HASNA_UPTIME_HOSTED_SQLITE_DB on cloud-mounted storage for the current hosted deployment path");
|
|
147
149
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
const hostedSqliteDbPath = options.hostedSqliteDbPath ?? process.env.HASNA_UPTIME_HOSTED_SQLITE_DB;
|
|
151
|
+
if (this.mode === "hosted" && hostedSqliteDbPath) {
|
|
152
|
+
if (hostedSqliteDbPath === ":memory:" || !hostedSqliteDbPath.startsWith("/")) {
|
|
153
|
+
throw new Error("HASNA_UPTIME_HOSTED_SQLITE_DB must be an absolute path on mounted cloud storage");
|
|
154
|
+
}
|
|
155
|
+
const approvedHostedPath = hostedSqliteDbPath === DEFAULT_HOSTED_SQLITE_DB_PATH;
|
|
156
|
+
if (!approvedHostedPath && !allowHostedLocalStore(options.allowHostedLocalStore)) {
|
|
157
|
+
throw new Error(`HASNA_UPTIME_HOSTED_SQLITE_DB must be ${DEFAULT_HOSTED_SQLITE_DB_PATH}; set HASNA_UPTIME_ALLOW_HOSTED_LOCAL_STORE=1 only for explicit local fallback testing`);
|
|
158
|
+
}
|
|
159
|
+
const verifiedCloudMount = approvedHostedPath && isNfsMount(dirname(hostedSqliteDbPath));
|
|
160
|
+
if (approvedHostedPath && !verifiedCloudMount && !allowHostedLocalStore(options.allowHostedLocalStore)) {
|
|
161
|
+
throw new Error(`${DEFAULT_HOSTED_SQLITE_DB_PATH} must be on a mounted EFS/NFS filesystem; refusing to create hosted task-local SQLite`);
|
|
162
|
+
}
|
|
163
|
+
this.dataMode = verifiedCloudMount ? "hosted-efs-sqlite" : "hosted-local-sqlite";
|
|
164
|
+
this.dbPath = hostedSqliteDbPath;
|
|
165
|
+
} else if (this.mode === "hosted") {
|
|
166
|
+
if (!allowHostedLocalStore(options.allowHostedLocalStore)) {
|
|
167
|
+
throw new Error("hosted mode requires HASNA_UPTIME_HOSTED_SQLITE_DB on mounted cloud storage; set HASNA_UPTIME_ALLOW_HOSTED_LOCAL_STORE=1 only for explicit local fallback testing");
|
|
168
|
+
}
|
|
169
|
+
this.dataMode = "hosted-local-sqlite";
|
|
170
|
+
this.dbPath = options.dbPath ?? uptimeHostedFallbackDbPath();
|
|
171
|
+
} else {
|
|
172
|
+
this.dataMode = "local-sqlite";
|
|
173
|
+
this.dbPath = options.dbPath ?? uptimeDbPath();
|
|
150
174
|
}
|
|
151
|
-
|
|
152
|
-
this.dbPath = options.dbPath ?? (this.mode === "hosted" ? uptimeHostedFallbackDbPath() : uptimeDbPath());
|
|
153
|
-
if (this.dbPath !== ":memory:") {
|
|
175
|
+
if (this.dbPath !== ":memory:" && this.dataMode !== "hosted-efs-sqlite") {
|
|
154
176
|
mkdirSync2(dirname(this.dbPath), { recursive: true });
|
|
155
177
|
}
|
|
156
178
|
this.db = new Database(this.dbPath, { create: true });
|
|
157
|
-
this.db.run("PRAGMA journal_mode = WAL");
|
|
179
|
+
this.db.run(this.dataMode === "hosted-efs-sqlite" ? "PRAGMA journal_mode = DELETE" : "PRAGMA journal_mode = WAL");
|
|
180
|
+
this.db.run("PRAGMA busy_timeout = 5000");
|
|
158
181
|
this.db.run("PRAGMA foreign_keys = ON");
|
|
159
182
|
this.migrate();
|
|
160
183
|
}
|
|
@@ -1034,6 +1057,13 @@ function resolveRuntimeMode(mode) {
|
|
|
1034
1057
|
function allowHostedLocalStore(value) {
|
|
1035
1058
|
return value === true || process.env.HASNA_UPTIME_ALLOW_HOSTED_LOCAL_STORE === "1";
|
|
1036
1059
|
}
|
|
1060
|
+
function isNfsMount(path) {
|
|
1061
|
+
try {
|
|
1062
|
+
return statfsSync(path).type === NFS_SUPER_MAGIC;
|
|
1063
|
+
} catch {
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1037
1067
|
function verifyBackupFile(backupPath) {
|
|
1038
1068
|
const db = new Database(backupPath, { readonly: true });
|
|
1039
1069
|
try {
|
|
@@ -1581,5 +1611,6 @@ function round(value, places) {
|
|
|
1581
1611
|
export {
|
|
1582
1612
|
resolveRuntimeMode,
|
|
1583
1613
|
UptimeStore,
|
|
1584
|
-
StaleCheckResultError
|
|
1614
|
+
StaleCheckResultError,
|
|
1615
|
+
DEFAULT_HOSTED_SQLITE_DB_PATH
|
|
1585
1616
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# AWS Deployment Runbook
|
|
2
2
|
|
|
3
|
-
This runbook is for
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
This runbook is for a reviewed AWS account target. It is intentionally dry-run
|
|
4
|
+
first: the local generator produces a plan and command list, but it does not
|
|
5
|
+
call AWS or mutate infrastructure.
|
|
6
6
|
|
|
7
7
|
## Generate The Plan
|
|
8
8
|
|
|
@@ -11,16 +11,17 @@ uptime cloud plan --json > open-uptime-aws-plan.json
|
|
|
11
11
|
uptime cloud spark01-config --probe-id prb_spark01 --env > spark01-uptime.env
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Public package defaults are placeholders:
|
|
15
15
|
|
|
16
|
-
- account/profile label: `
|
|
16
|
+
- account/profile label: `aws-profile`
|
|
17
17
|
- region: `us-east-1`
|
|
18
|
-
- VPC: `vpc-
|
|
19
|
-
-
|
|
20
|
-
- hostname: `uptime.
|
|
21
|
-
- workspace id: `
|
|
18
|
+
- VPC: `vpc-xxxxxxxx`
|
|
19
|
+
- hosted data path: EFS-mounted SQLite at `/data/uptime/uptime.db`
|
|
20
|
+
- hostname: `uptime.example.com`
|
|
21
|
+
- workspace id: `workspace-id`
|
|
22
22
|
|
|
23
|
-
Override these with CLI flags
|
|
23
|
+
Override these with CLI flags or private deployment evidence for the real
|
|
24
|
+
account, hostname, workspace id, VPC id, secret refs, and repository names.
|
|
24
25
|
|
|
25
26
|
The generated AWS plan currently returns `status: "blocked"` and
|
|
26
27
|
`canApply: false`. The generated Spark01 config returns `status: "blocked"` and
|
|
@@ -36,15 +37,16 @@ write a sourceable env file with a placeholder probe identity.
|
|
|
36
37
|
|
|
37
38
|
## Preflight
|
|
38
39
|
|
|
39
|
-
1. Locate the real
|
|
40
|
-
|
|
40
|
+
1. Locate the real infrastructure repository or create the change in the
|
|
41
|
+
approved owner repository.
|
|
41
42
|
2. Confirm the AWS caller identity:
|
|
42
43
|
|
|
43
44
|
```bash
|
|
44
|
-
aws sts get-caller-identity --profile
|
|
45
|
+
aws sts get-caller-identity --profile <aws-profile>
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
3. Confirm the target VPC
|
|
48
|
+
3. Confirm the target VPC, private subnets, KMS key, and EFS/Backup plan inputs
|
|
49
|
+
still match the plan.
|
|
48
50
|
4. Confirm Route53/edge ownership for the chosen hostname.
|
|
49
51
|
5. Confirm the deployment role uses short-lived credentials or OIDC, not copied
|
|
50
52
|
access keys.
|
|
@@ -55,11 +57,13 @@ The plan expects:
|
|
|
55
57
|
|
|
56
58
|
- ECR repository for the Open Uptime image.
|
|
57
59
|
- ECS/Fargate cluster with separate services for web, scheduler, public probe,
|
|
58
|
-
reporter, and one-off migrations.
|
|
60
|
+
reporter, and one-off migrations. In the current EFS SQLite bridge, only web
|
|
61
|
+
may be enabled and it must run at desired count `0` or `1`.
|
|
59
62
|
- ALB, TLS certificate, target group, and security groups.
|
|
60
|
-
-
|
|
63
|
+
- Encrypted EFS file system, access point, mount targets, and AWS Backup plan
|
|
64
|
+
for `HASNA_UPTIME_HOSTED_SQLITE_DB=/data/uptime/uptime.db`.
|
|
61
65
|
- S3 bucket for redacted browser evidence and generated report artifacts.
|
|
62
|
-
- Secrets Manager or SSM refs for
|
|
66
|
+
- Secrets Manager or SSM refs for app env, hosted token, probe config, and
|
|
63
67
|
reporting channel refs.
|
|
64
68
|
- CloudWatch log groups for every component plus initial web 5xx/unhealthy
|
|
65
69
|
alarms. Scheduler-stall, stale-probe, and report-delivery alarms remain
|
|
@@ -91,10 +95,15 @@ routes are backed by cloud check jobs and cloud audit rows.
|
|
|
91
95
|
## Safety Rules
|
|
92
96
|
|
|
93
97
|
- Do not deploy hosted mode with `HASNA_UPTIME_ALLOW_HOSTED_LOCAL_STORE=1`.
|
|
98
|
+
- Do deploy hosted mode with `HASNA_UPTIME_HOSTED_SQLITE_DB` pointing at the EFS
|
|
99
|
+
mount path `/data/uptime/uptime.db`. Do not set `HASNA_UPTIME_DATABASE_URL`
|
|
100
|
+
until the async Postgres adapter exists.
|
|
94
101
|
- Do not inline AWS keys, hosted tokens, Mailery keys, Open Logs tokens, database
|
|
95
102
|
URLs, or probe private keys in task definitions. Use ECS `secrets.valueFrom`
|
|
96
|
-
refs such as `
|
|
103
|
+
refs such as `HASNA_UPTIME_HOSTED_TOKEN`.
|
|
97
104
|
- Do not run public probe workers against private targets.
|
|
105
|
+
- Do not enable scheduler, public-probe, reporter, or migration workers against
|
|
106
|
+
the EFS SQLite bridge; those services need Postgres/cloud leases first.
|
|
98
107
|
- Do not expose dashboard/API routes without hosted auth and workspace checks.
|
|
99
108
|
- Do not treat local SQLite, local project DBs, or Spark01 local state as cloud
|
|
100
109
|
authority after cutover.
|
|
@@ -103,5 +112,6 @@ routes are backed by cloud check jobs and cloud audit rows.
|
|
|
103
112
|
|
|
104
113
|
Before each service update, record the previous task definition ARN. Roll back
|
|
105
114
|
by disabling scheduler/reporter work first, then restoring the previous web or
|
|
106
|
-
worker task definition.
|
|
107
|
-
|
|
115
|
+
worker task definition. EFS backup restore requires separate operator approval,
|
|
116
|
+
a selected recovery point, a replacement mount target/access point cutover, and
|
|
117
|
+
an audit event.
|
package/infra/aws/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Open Uptime AWS Infra
|
|
2
2
|
|
|
3
3
|
This directory is a reviewable Terraform/OpenTofu starting point for deploying
|
|
4
|
-
Open Uptime in
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
Open Uptime in a reviewed AWS account. It is intentionally plan-first. Do not
|
|
5
|
+
apply it directly from this app repo unless the infrastructure owner has
|
|
6
|
+
approved this directory as the source of truth or has copied it into the
|
|
7
|
+
approved infra repository.
|
|
8
8
|
|
|
9
9
|
## Expected Flow
|
|
10
10
|
|
|
@@ -20,13 +20,24 @@ Required inputs are declared in `variables.tf` and illustrated in
|
|
|
20
20
|
never place plaintext tokens, database URLs, private keys, or channel
|
|
21
21
|
credentials in `.tfvars` files.
|
|
22
22
|
|
|
23
|
+
The current deployable hosted state path is EFS-backed SQLite mounted at
|
|
24
|
+
`/data/uptime/uptime.db` and wired through `HASNA_UPTIME_HOSTED_SQLITE_DB` for
|
|
25
|
+
one protected web task maximum. Scheduler, public-probe, reporter, and migration
|
|
26
|
+
must remain at desired count `0` and receive no EFS mount until the Postgres
|
|
27
|
+
adapter and cloud leases are implemented. Do not set
|
|
28
|
+
`HASNA_UPTIME_DATABASE_URL` for hosted ECS tasks until then.
|
|
29
|
+
|
|
30
|
+
The included CodeBuild project builds `@hasna/uptime` from npm with
|
|
31
|
+
`Dockerfile.package` and pushes the resulting image to ECR. This avoids
|
|
32
|
+
depending on a local Docker daemon for image publication.
|
|
33
|
+
|
|
23
34
|
## Current Blockers
|
|
24
35
|
|
|
25
|
-
- Hosted Postgres adapter and migrations are not implemented in the app yet.
|
|
26
36
|
- Hosted production auth/RBAC still needs scoped, revocable credentials.
|
|
27
37
|
- Public probe runtime still needs execution-time DNS/redirect/rebinding SSRF
|
|
28
38
|
enforcement.
|
|
29
39
|
- Spark01 hosted private-probe enrollment/heartbeat/revocation is still
|
|
30
40
|
fail-closed.
|
|
31
41
|
|
|
32
|
-
Keep `desired_count` at `0
|
|
42
|
+
Keep `desired_count` at `0`, or at `1` for the protected web bridge only after
|
|
43
|
+
review evidence exists, until those blockers are closed.
|