@hasna/uptime 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,24 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.15] - 2026-06-28
10
+
11
+ ### Changed
12
+
13
+ - Changed the packaged production Dockerfile to use Docker Official
14
+ `node:22-slim` from Amazon ECR Public and install Bun inside the image. This
15
+ avoids Docker Hub unauthenticated pull-rate limits during AWS CodeBuild image
16
+ builds.
17
+
18
+ ## [0.1.14] - 2026-06-28
19
+
20
+ ### Added
21
+
22
+ - Added explicit Terraform NAT task egress support. Infra owners can set
23
+ `enable_nat_task_egress = true` to allow web and non-public worker task
24
+ security groups to reach AWS public APIs through a private subnet NAT route on
25
+ TCP/443 when private VPC endpoints are not the approved egress model.
26
+
9
27
  ## [0.1.13] - 2026-06-28
10
28
 
11
29
  ### Added
@@ -1,17 +1,19 @@
1
1
  # syntax=docker/dockerfile:1
2
2
 
3
- FROM oven/bun:1.3.13-slim AS runtime
3
+ FROM public.ecr.aws/docker/library/node:22-slim AS runtime
4
4
  ENV NODE_ENV=production \
5
5
  HASNA_UPTIME_MODE=hosted
6
6
  WORKDIR /app
7
7
 
8
- RUN addgroup --system --gid 10001 uptime \
9
- && adduser --system --uid 10001 --ingroup uptime uptime
8
+ RUN npm install -g bun@1.3.13 \
9
+ && groupadd --system --gid 10001 uptime \
10
+ && useradd --system --uid 10001 --gid uptime --home-dir /app --shell /usr/sbin/nologin uptime
10
11
 
11
12
  COPY package.json ./package.json
12
13
  COPY dist ./dist
13
14
 
14
- RUN bun install --production
15
+ RUN bun install --production \
16
+ && chown -R uptime:uptime /app
15
17
 
16
18
  USER uptime
17
19
  EXPOSE 3899
package/dist/cli/index.js CHANGED
@@ -6932,7 +6932,7 @@ function buildAwsDeploymentPlan(options = {}) {
6932
6932
  const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
6933
6933
  const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
6934
6934
  const hostedSqliteDbPath = clean(options.hostedSqliteDbPath, DEFAULT_HOSTED_SQLITE_DB);
6935
- const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.13");
6935
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.15");
6936
6936
  const protectedAccessMode = options.protectedAccessMode ?? DEFAULT_PROTECTED_ACCESS_MODE;
6937
6937
  const protectedAccessUrl = protectedAccessMode === "cloudfront_default_domain" ? "https://<cloudfront-domain>" : `https://${hostname}`;
6938
6938
  const cluster = `${prefix}-${stage}`;
@@ -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.13");
24
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.15");
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}`;
package/dist/index.js CHANGED
@@ -4338,7 +4338,7 @@ function buildAwsDeploymentPlan(options = {}) {
4338
4338
  const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
4339
4339
  const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
4340
4340
  const hostedSqliteDbPath = clean(options.hostedSqliteDbPath, DEFAULT_HOSTED_SQLITE_DB);
4341
- const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.13");
4341
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.15");
4342
4342
  const protectedAccessMode = options.protectedAccessMode ?? DEFAULT_PROTECTED_ACCESS_MODE;
4343
4343
  const protectedAccessUrl = protectedAccessMode === "cloudfront_default_domain" ? "https://<cloudfront-domain>" : `https://${hostname}`;
4344
4344
  const cluster = `${prefix}-${stage}`;
@@ -64,6 +64,13 @@ Endpoint policies are scoped to the Open Uptime repository, log groups,
64
64
  configured secret refs, KMS key, evidence bucket, and the regional ECR layer
65
65
  bucket.
66
66
 
67
+ If private endpoints are not approved yet, infra owners can instead set
68
+ `enable_nat_task_egress = true` to allow web and non-public worker task security
69
+ groups to reach AWS public APIs through the private subnet NAT route on TCP/443.
70
+ Keep this disabled when private endpoints are the approved egress path. Runtime
71
+ scale-up still requires ECS task evidence for image pull, secret injection, log
72
+ delivery, S3 access, and EFS mount behavior.
73
+
67
74
  Interface endpoint private DNS is VPC-wide. In shared VPCs, either keep endpoint
68
75
  creation in the approved networking root, or pass
69
76
  `additional_vpc_endpoint_source_security_group_ids` for every workload that must
package/infra/aws/main.tf CHANGED
@@ -399,6 +399,32 @@ resource "aws_security_group_rule" "worker_egress" {
399
399
  cidr_blocks = each.key == "public-probe" ? ["0.0.0.0/0"] : [data.aws_vpc.target.cidr_block]
400
400
  }
401
401
 
402
+ resource "aws_security_group_rule" "web_nat_https_egress" {
403
+ count = var.enable_nat_task_egress ? 1 : 0
404
+
405
+ type = "egress"
406
+ description = "HTTPS egress through approved NAT path"
407
+ security_group_id = aws_security_group.web.id
408
+ from_port = 443
409
+ to_port = 443
410
+ protocol = "tcp"
411
+ cidr_blocks = var.nat_task_egress_cidr_blocks
412
+ }
413
+
414
+ resource "aws_security_group_rule" "worker_nat_https_egress" {
415
+ for_each = var.enable_nat_task_egress ? {
416
+ for key, value in aws_security_group.worker : key => value if key != "public-probe"
417
+ } : {}
418
+
419
+ type = "egress"
420
+ description = "HTTPS egress through approved NAT path"
421
+ security_group_id = each.value.id
422
+ from_port = 443
423
+ to_port = 443
424
+ protocol = "tcp"
425
+ cidr_blocks = var.nat_task_egress_cidr_blocks
426
+ }
427
+
402
428
  resource "aws_security_group_rule" "web_s3_gateway_egress" {
403
429
  count = local.s3_gateway_endpoint_enabled ? 1 : 0
404
430
 
@@ -16,7 +16,7 @@ alb_ingress_cidr_blocks = []
16
16
  private_subnet_ids = ["subnet-replace-private-a", "subnet-replace-private-b"]
17
17
  private_route_table_ids = ["rtb-replace-private"]
18
18
  container_image = "123456789012.dkr.ecr.us-east-1.amazonaws.com/open-uptime@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
19
- runtime_package_version = "0.1.13"
19
+ runtime_package_version = "0.1.15"
20
20
  certificate_arn = null
21
21
  hosted_zone_id = null
22
22
  app_env_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:open-uptime/prod/app/env"
@@ -26,6 +26,7 @@ reporting_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret
26
26
  kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/00000000-0000-0000-0000-000000000000"
27
27
  alarm_actions = []
28
28
  monthly_budget_limit_usd = 0
29
+ enable_nat_task_egress = false
29
30
  enable_private_vpc_endpoints = false
30
31
  additional_vpc_endpoint_source_security_group_ids = []
31
32
 
@@ -116,7 +116,7 @@ variable "container_image" {
116
116
  variable "runtime_package_version" {
117
117
  description = "Published @hasna/uptime package version that CodeBuild should build into the ECR image."
118
118
  type = string
119
- default = "0.1.13"
119
+ default = "0.1.15"
120
120
 
121
121
  validation {
122
122
  condition = can(regex("^[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9A-Za-z.-]+)?$", var.runtime_package_version))
@@ -238,6 +238,23 @@ variable "budget_alert_email_addresses" {
238
238
  default = []
239
239
  }
240
240
 
241
+ variable "enable_nat_task_egress" {
242
+ description = "Allow web and non-public worker tasks to reach AWS public APIs through NAT on TCP/443. Keep false when private VPC endpoints are the approved egress path."
243
+ type = bool
244
+ default = false
245
+ }
246
+
247
+ variable "nat_task_egress_cidr_blocks" {
248
+ description = "CIDR blocks allowed for NAT-backed HTTPS egress when enable_nat_task_egress is true."
249
+ type = list(string)
250
+ default = ["0.0.0.0/0"]
251
+
252
+ validation {
253
+ condition = length(var.nat_task_egress_cidr_blocks) > 0
254
+ error_message = "nat_task_egress_cidr_blocks must not be empty when NAT task egress is enabled."
255
+ }
256
+ }
257
+
241
258
  variable "enable_private_vpc_endpoints" {
242
259
  description = "Create private VPC endpoints for ECS access to AWS APIs. Requires private subnet ids; S3 gateway endpoint also requires private_route_table_ids."
243
260
  type = bool
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/uptime",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Local-first uptime and downtime monitoring service with CLI, MCP, SDK, SQLite persistence, and a dashboard.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",