@hasna/uptime 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.13] - 2026-06-28
10
+
11
+ ### Added
12
+
13
+ - Added opt-in Terraform support for private AWS VPC endpoints. Infra owners can
14
+ enable interface endpoints for ECR API, ECR Docker, CloudWatch Logs, and
15
+ Secrets Manager, plus an S3 gateway endpoint when private route tables are
16
+ provided.
17
+
9
18
  ## [0.1.12] - 2026-06-28
10
19
 
11
20
  ### Added
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.12");
6935
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.13");
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.12");
24
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.13");
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.12");
4341
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.13");
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}`;
@@ -80,8 +80,10 @@ The plan expects:
80
80
  - Encrypted EFS file system, access point, mount targets, and AWS Backup plan
81
81
  for `HASNA_UPTIME_HOSTED_SQLITE_DB=/data/uptime/uptime.db`.
82
82
  - S3 bucket for redacted browser evidence and generated report artifacts.
83
- - Secrets Manager or SSM refs for app env, hosted token, probe config, and
84
- reporting channel refs.
83
+ - Secrets Manager refs for app env, hosted token, probe config, and reporting
84
+ channel refs. If any ECS secret uses an SSM Parameter Store ARN, add `ssm` to
85
+ `interface_vpc_endpoint_services` or document the approved alternate egress
86
+ path before running private-only tasks.
85
87
  - CloudWatch log groups for every component plus initial web 5xx/unhealthy
86
88
  alarms. Scheduler-stall, stale-probe, and report-delivery alarms remain
87
89
  blocked until those workers emit cloud metrics.
@@ -176,8 +178,12 @@ Before setting `desired_counts.web = 1`, verify:
176
178
  - `HASNA_UPTIME_ALLOWED_ORIGINS` matches the public HTTPS edge origin;
177
179
  - CloudFront origin access is distribution-bound, not just narrowed to
178
180
  CloudFront origin-facing ranges;
179
- - web egress to ECR, Secrets Manager, CloudWatch Logs, S3, EFS, and any required
180
- endpoints has been proven through NAT or VPC endpoints;
181
+ - web egress to ECR, Secrets Manager or SSM, CloudWatch Logs, S3, EFS, and any
182
+ required endpoints has been proven from a real ECS task. Terraform endpoint
183
+ ids, route tables, and security-group rules are creation evidence only; the
184
+ scale-up evidence must include image pull, secret injection, log delivery, S3
185
+ access, and EFS mount checks through the selected NAT or private-endpoint
186
+ path;
181
187
  - scheduler, public-probe, reporter, and migration remain at `0`.
182
188
 
183
189
  Scale only the web task, then capture the ECS deployment id and task definition
@@ -52,6 +52,25 @@ type, and cost-center tags. Set `monthly_budget_limit_usd` plus
52
52
  forecasted and actual spend alerts. Leaving the email list empty skips budget
53
53
  creation and is not sufficient for live scale-out approval.
54
54
 
55
+ Private AWS API egress can be pinned through opt-in VPC endpoints by setting
56
+ `enable_private_vpc_endpoints = true` and passing `private_route_table_ids`.
57
+ This creates interface endpoints for ECR API, ECR Docker, CloudWatch Logs, and
58
+ Secrets Manager, plus an S3 gateway endpoint when route tables are supplied. The
59
+ default is `false` so package consumers do not create endpoint hourly cost
60
+ without explicit infra-owner approval. The S3 gateway endpoint is required for
61
+ private ECR image layer pulls; the module adds S3 managed-prefix-list egress for
62
+ web and non-public worker security groups when the gateway endpoint is enabled.
63
+ Endpoint policies are scoped to the Open Uptime repository, log groups,
64
+ configured secret refs, KMS key, evidence bucket, and the regional ECR layer
65
+ bucket.
66
+
67
+ Interface endpoint private DNS is VPC-wide. In shared VPCs, either keep endpoint
68
+ creation in the approved networking root, or pass
69
+ `additional_vpc_endpoint_source_security_group_ids` for every workload that must
70
+ keep using those private DNS names. If any ECS secret ref uses SSM Parameter
71
+ Store instead of Secrets Manager, add `ssm` to
72
+ `interface_vpc_endpoint_services` or keep an approved non-endpoint egress path.
73
+
55
74
  ## Current Blockers
56
75
 
57
76
  - Hosted production auth/RBAC still needs scoped, revocable credentials.
package/infra/aws/main.tf CHANGED
@@ -14,6 +14,7 @@ provider "aws" {
14
14
  }
15
15
 
16
16
  data "aws_caller_identity" "current" {}
17
+ data "aws_partition" "current" {}
17
18
 
18
19
  locals {
19
20
  prefix = "${var.service_name}-${var.stage}"
@@ -63,6 +64,21 @@ locals {
63
64
  AppType = var.app_type
64
65
  CostCenter = var.cost_center
65
66
  }
67
+ s3_gateway_endpoint_enabled = var.enable_private_vpc_endpoints && contains(var.gateway_vpc_endpoint_services, "s3") && length(var.private_route_table_ids) > 0
68
+ endpoint_secret_refs = distinct(flatten([for service in values(local.services) : values(service.secrets)]))
69
+ secretsmanager_secret_refs = [for ref in local.endpoint_secret_refs : ref if can(regex(":secretsmanager:", ref))]
70
+ ssm_parameter_refs = [for ref in local.endpoint_secret_refs : ref if can(regex(":ssm:", ref))]
71
+ secretsmanager_policy_refs = (
72
+ length(local.secretsmanager_secret_refs) > 0
73
+ ? local.secretsmanager_secret_refs
74
+ : ["arn:${data.aws_partition.current.partition}:secretsmanager:${var.region}:${data.aws_caller_identity.current.account_id}:secret:${local.prefix}/no-secretsmanager-refs-configured-*"]
75
+ )
76
+ ssm_policy_refs = (
77
+ length(local.ssm_parameter_refs) > 0
78
+ ? local.ssm_parameter_refs
79
+ : ["arn:${data.aws_partition.current.partition}:ssm:${var.region}:${data.aws_caller_identity.current.account_id}:parameter/${local.prefix}/no-ssm-refs-configured"]
80
+ )
81
+ service_log_group_arns = [for group in aws_cloudwatch_log_group.service : "${group.arn}:*"]
66
82
  }
67
83
 
68
84
  data "aws_vpc" "target" {
@@ -383,6 +399,278 @@ resource "aws_security_group_rule" "worker_egress" {
383
399
  cidr_blocks = each.key == "public-probe" ? ["0.0.0.0/0"] : [data.aws_vpc.target.cidr_block]
384
400
  }
385
401
 
402
+ resource "aws_security_group_rule" "web_s3_gateway_egress" {
403
+ count = local.s3_gateway_endpoint_enabled ? 1 : 0
404
+
405
+ type = "egress"
406
+ description = "HTTPS to S3 gateway endpoint prefix list"
407
+ security_group_id = aws_security_group.web.id
408
+ from_port = 443
409
+ to_port = 443
410
+ protocol = "tcp"
411
+ prefix_list_ids = [aws_vpc_endpoint.gateway["s3"].prefix_list_id]
412
+ }
413
+
414
+ resource "aws_security_group_rule" "worker_s3_gateway_egress" {
415
+ for_each = local.s3_gateway_endpoint_enabled ? {
416
+ for key, value in aws_security_group.worker : key => value if key != "public-probe"
417
+ } : {}
418
+
419
+ type = "egress"
420
+ description = "HTTPS to S3 gateway endpoint prefix list"
421
+ security_group_id = each.value.id
422
+ from_port = 443
423
+ to_port = 443
424
+ protocol = "tcp"
425
+ prefix_list_ids = [aws_vpc_endpoint.gateway["s3"].prefix_list_id]
426
+ }
427
+
428
+ resource "aws_security_group" "vpc_endpoints" {
429
+ count = var.enable_private_vpc_endpoints ? 1 : 0
430
+ name = "${local.prefix}-vpc-endpoints-sg"
431
+ description = "Open Uptime interface VPC endpoints"
432
+ vpc_id = data.aws_vpc.target.id
433
+ tags = merge(local.tags, { Component = "vpc-endpoints" })
434
+ }
435
+
436
+ resource "aws_security_group_rule" "vpc_endpoints_from_web" {
437
+ count = var.enable_private_vpc_endpoints ? 1 : 0
438
+ type = "ingress"
439
+ description = "HTTPS from Open Uptime web tasks"
440
+ security_group_id = aws_security_group.vpc_endpoints[0].id
441
+ from_port = 443
442
+ to_port = 443
443
+ protocol = "tcp"
444
+ source_security_group_id = aws_security_group.web.id
445
+ }
446
+
447
+ resource "aws_security_group_rule" "vpc_endpoints_from_worker" {
448
+ for_each = var.enable_private_vpc_endpoints ? aws_security_group.worker : {}
449
+
450
+ type = "ingress"
451
+ description = "HTTPS from Open Uptime ${each.key} tasks"
452
+ security_group_id = aws_security_group.vpc_endpoints[0].id
453
+ from_port = 443
454
+ to_port = 443
455
+ protocol = "tcp"
456
+ source_security_group_id = each.value.id
457
+ }
458
+
459
+ resource "aws_security_group_rule" "vpc_endpoints_from_additional_sources" {
460
+ for_each = var.enable_private_vpc_endpoints ? toset(var.additional_vpc_endpoint_source_security_group_ids) : toset([])
461
+
462
+ type = "ingress"
463
+ description = "HTTPS from additional approved source security group"
464
+ security_group_id = aws_security_group.vpc_endpoints[0].id
465
+ from_port = 443
466
+ to_port = 443
467
+ protocol = "tcp"
468
+ source_security_group_id = each.value
469
+ }
470
+
471
+ data "aws_iam_policy_document" "vpc_endpoint_ecr_api" {
472
+ statement {
473
+ sid = "AllowEcrAuthorization"
474
+ actions = ["ecr:GetAuthorizationToken"]
475
+ resources = ["*"]
476
+
477
+ principals {
478
+ type = "*"
479
+ identifiers = ["*"]
480
+ }
481
+ }
482
+
483
+ statement {
484
+ sid = "AllowOpenUptimeRepositoryRead"
485
+ actions = [
486
+ "ecr:BatchCheckLayerAvailability",
487
+ "ecr:BatchGetImage",
488
+ "ecr:DescribeImages",
489
+ "ecr:DescribeRepositories",
490
+ "ecr:GetDownloadUrlForLayer",
491
+ ]
492
+ resources = [aws_ecr_repository.open_uptime.arn]
493
+
494
+ principals {
495
+ type = "*"
496
+ identifiers = ["*"]
497
+ }
498
+ }
499
+ }
500
+
501
+ data "aws_iam_policy_document" "vpc_endpoint_ecr_dkr" {
502
+ statement {
503
+ sid = "AllowOpenUptimeRegistryRead"
504
+ actions = [
505
+ "ecr:BatchCheckLayerAvailability",
506
+ "ecr:BatchGetImage",
507
+ "ecr:GetDownloadUrlForLayer",
508
+ ]
509
+ resources = [aws_ecr_repository.open_uptime.arn]
510
+
511
+ principals {
512
+ type = "*"
513
+ identifiers = ["*"]
514
+ }
515
+ }
516
+ }
517
+
518
+ data "aws_iam_policy_document" "vpc_endpoint_logs" {
519
+ statement {
520
+ sid = "AllowOpenUptimeLogDelivery"
521
+ actions = [
522
+ "logs:CreateLogStream",
523
+ "logs:DescribeLogStreams",
524
+ "logs:PutLogEvents",
525
+ ]
526
+ resources = local.service_log_group_arns
527
+
528
+ principals {
529
+ type = "*"
530
+ identifiers = ["*"]
531
+ }
532
+ }
533
+ }
534
+
535
+ data "aws_iam_policy_document" "vpc_endpoint_secretsmanager" {
536
+ statement {
537
+ sid = "AllowOpenUptimeSecretReads"
538
+ actions = [
539
+ "secretsmanager:DescribeSecret",
540
+ "secretsmanager:GetSecretValue",
541
+ ]
542
+ resources = local.secretsmanager_policy_refs
543
+
544
+ principals {
545
+ type = "*"
546
+ identifiers = ["*"]
547
+ }
548
+ }
549
+ }
550
+
551
+ data "aws_iam_policy_document" "vpc_endpoint_ssm" {
552
+ statement {
553
+ sid = "AllowOpenUptimeParameterReads"
554
+ actions = [
555
+ "ssm:GetParameter",
556
+ "ssm:GetParameters",
557
+ ]
558
+ resources = local.ssm_policy_refs
559
+
560
+ principals {
561
+ type = "*"
562
+ identifiers = ["*"]
563
+ }
564
+ }
565
+ }
566
+
567
+ data "aws_iam_policy_document" "vpc_endpoint_sts" {
568
+ statement {
569
+ sid = "AllowCallerIdentity"
570
+ actions = ["sts:GetCallerIdentity"]
571
+ resources = ["*"]
572
+
573
+ principals {
574
+ type = "*"
575
+ identifiers = ["*"]
576
+ }
577
+ }
578
+ }
579
+
580
+ data "aws_iam_policy_document" "vpc_endpoint_kms" {
581
+ statement {
582
+ sid = "AllowOpenUptimeKeyUse"
583
+ actions = [
584
+ "kms:Decrypt",
585
+ "kms:DescribeKey",
586
+ "kms:GenerateDataKey*",
587
+ ]
588
+ resources = [var.kms_key_arn]
589
+
590
+ principals {
591
+ type = "*"
592
+ identifiers = ["*"]
593
+ }
594
+ }
595
+ }
596
+
597
+ data "aws_iam_policy_document" "vpc_endpoint_s3" {
598
+ statement {
599
+ sid = "AllowOpenUptimeEvidenceBucket"
600
+ actions = [
601
+ "s3:AbortMultipartUpload",
602
+ "s3:GetBucketLocation",
603
+ "s3:GetObject",
604
+ "s3:ListBucket",
605
+ "s3:PutObject",
606
+ ]
607
+ resources = [
608
+ aws_s3_bucket.evidence.arn,
609
+ "${aws_s3_bucket.evidence.arn}/*",
610
+ ]
611
+
612
+ principals {
613
+ type = "*"
614
+ identifiers = ["*"]
615
+ }
616
+ }
617
+
618
+ statement {
619
+ sid = "AllowEcrLayerBucket"
620
+ actions = ["s3:GetObject"]
621
+ resources = ["arn:${data.aws_partition.current.partition}:s3:::prod-${var.region}-starport-layer-bucket/*"]
622
+
623
+ principals {
624
+ type = "*"
625
+ identifiers = ["*"]
626
+ }
627
+ }
628
+ }
629
+
630
+ resource "aws_vpc_endpoint" "interface" {
631
+ for_each = var.enable_private_vpc_endpoints ? toset(var.interface_vpc_endpoint_services) : toset([])
632
+
633
+ vpc_id = data.aws_vpc.target.id
634
+ service_name = "com.amazonaws.${var.region}.${each.key}"
635
+ vpc_endpoint_type = "Interface"
636
+ subnet_ids = var.private_subnet_ids
637
+ security_group_ids = [aws_security_group.vpc_endpoints[0].id]
638
+ private_dns_enabled = true
639
+ policy = {
640
+ "ecr.api" = data.aws_iam_policy_document.vpc_endpoint_ecr_api.json
641
+ "ecr.dkr" = data.aws_iam_policy_document.vpc_endpoint_ecr_dkr.json
642
+ logs = data.aws_iam_policy_document.vpc_endpoint_logs.json
643
+ secretsmanager = data.aws_iam_policy_document.vpc_endpoint_secretsmanager.json
644
+ ssm = data.aws_iam_policy_document.vpc_endpoint_ssm.json
645
+ sts = data.aws_iam_policy_document.vpc_endpoint_sts.json
646
+ kms = data.aws_iam_policy_document.vpc_endpoint_kms.json
647
+ }[each.key]
648
+
649
+ tags = merge(local.tags, {
650
+ Name = "${local.prefix}-${replace(each.key, ".", "-")}-endpoint"
651
+ Component = "vpc-endpoint"
652
+ Endpoint = each.key
653
+ })
654
+ }
655
+
656
+ resource "aws_vpc_endpoint" "gateway" {
657
+ for_each = var.enable_private_vpc_endpoints && length(var.private_route_table_ids) > 0 ? toset(var.gateway_vpc_endpoint_services) : toset([])
658
+
659
+ vpc_id = data.aws_vpc.target.id
660
+ service_name = "com.amazonaws.${var.region}.${each.key}"
661
+ vpc_endpoint_type = "Gateway"
662
+ route_table_ids = var.private_route_table_ids
663
+ policy = {
664
+ s3 = data.aws_iam_policy_document.vpc_endpoint_s3.json
665
+ }[each.key]
666
+
667
+ tags = merge(local.tags, {
668
+ Name = "${local.prefix}-${each.key}-endpoint"
669
+ Component = "vpc-endpoint"
670
+ Endpoint = each.key
671
+ })
672
+ }
673
+
386
674
  resource "aws_security_group" "efs" {
387
675
  name = "${local.prefix}-efs-sg"
388
676
  description = "Open Uptime EFS data store"
@@ -75,3 +75,10 @@ output "service_names" {
75
75
  [for service in aws_ecs_service.worker : service.name],
76
76
  )
77
77
  }
78
+
79
+ output "vpc_endpoint_ids" {
80
+ value = {
81
+ interface = { for service, endpoint in aws_vpc_endpoint.interface : service => endpoint.id }
82
+ gateway = { for service, endpoint in aws_vpc_endpoint.gateway : service => endpoint.id }
83
+ }
84
+ }
@@ -14,8 +14,9 @@ protected_access_mode = "cloudfront_default_domain"
14
14
  public_subnet_ids = ["subnet-replace-public-a", "subnet-replace-public-b"]
15
15
  alb_ingress_cidr_blocks = []
16
16
  private_subnet_ids = ["subnet-replace-private-a", "subnet-replace-private-b"]
17
+ private_route_table_ids = ["rtb-replace-private"]
17
18
  container_image = "123456789012.dkr.ecr.us-east-1.amazonaws.com/open-uptime@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
18
- runtime_package_version = "0.1.12"
19
+ runtime_package_version = "0.1.13"
19
20
  certificate_arn = null
20
21
  hosted_zone_id = null
21
22
  app_env_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:open-uptime/prod/app/env"
@@ -25,6 +26,8 @@ reporting_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret
25
26
  kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/00000000-0000-0000-0000-000000000000"
26
27
  alarm_actions = []
27
28
  monthly_budget_limit_usd = 0
29
+ enable_private_vpc_endpoints = false
30
+ additional_vpc_endpoint_source_security_group_ids = []
28
31
 
29
32
  desired_counts = {
30
33
  web = 0
@@ -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.12"
119
+ default = "0.1.13"
120
120
 
121
121
  validation {
122
122
  condition = can(regex("^[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9A-Za-z.-]+)?$", var.runtime_package_version))
@@ -237,3 +237,45 @@ variable "budget_alert_email_addresses" {
237
237
  type = list(string)
238
238
  default = []
239
239
  }
240
+
241
+ variable "enable_private_vpc_endpoints" {
242
+ 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
+ type = bool
244
+ default = false
245
+ }
246
+
247
+ variable "interface_vpc_endpoint_services" {
248
+ description = "Regional interface endpoint service short names to create when enable_private_vpc_endpoints is true."
249
+ type = list(string)
250
+ default = ["ecr.api", "ecr.dkr", "logs", "secretsmanager"]
251
+
252
+ validation {
253
+ condition = alltrue([
254
+ for service in var.interface_vpc_endpoint_services : contains(["ecr.api", "ecr.dkr", "logs", "secretsmanager", "sts", "ssm", "kms"], service)
255
+ ])
256
+ error_message = "interface_vpc_endpoint_services must contain only approved AWS service short names."
257
+ }
258
+ }
259
+
260
+ variable "additional_vpc_endpoint_source_security_group_ids" {
261
+ description = "Additional source security groups allowed to use Open Uptime interface VPC endpoints in a shared VPC. Keep empty for dedicated Open Uptime subnets."
262
+ type = list(string)
263
+ default = []
264
+ }
265
+
266
+ variable "gateway_vpc_endpoint_services" {
267
+ description = "Regional gateway endpoint service short names to create when enable_private_vpc_endpoints is true."
268
+ type = list(string)
269
+ default = ["s3"]
270
+
271
+ validation {
272
+ condition = alltrue([for service in var.gateway_vpc_endpoint_services : contains(["s3"], service)])
273
+ error_message = "gateway_vpc_endpoint_services currently supports only s3."
274
+ }
275
+ }
276
+
277
+ variable "private_route_table_ids" {
278
+ description = "Private route table ids for gateway VPC endpoints such as S3. Leave empty to skip gateway endpoint creation."
279
+ type = list(string)
280
+ default = []
281
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/uptime",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
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",