SecurityV0 Infrastructure Strategy
Date: 2026-03-31 Status: Active
Update 2026-04-23: The platform-hosting target for T3 cutover is Azure VM (not "AWS Lambda → ECS Fargate" as written in §8 Phases 3–4). See
2026-04-23-autonomous-scans-and-built-in-validation.mdPillar C for rationale. The AWS Organization + SCP + sandbox-account scheme in §4–§6 of this doc remains fully valid — it concerns accounts the AWS connector scans, not where the platform runs. Docker Compose on a plain Linux VM stays as the deploy shape across any cloud cutover.
Executive Summary
SecurityV0 currently runs on a single Hetzner VPS (Docker Compose: API + UI + MongoDB). Connectors are manual CLI scripts. This is fine for development but blocks three things:
- Temporal findings — connectors must run continuously to detect drift and ownership decay
- AWS integration development — we need real AWS infrastructure to build and test the AWS connector
- Demo environments — we need ephemeral AWS accounts to simulate customer infrastructure
The strategy:
- Phase 1 (now): Activate $5,000 AWS credits and 1Password Business (both free via Mercury perks). Automate connectors on Hetzner with systemd timers + tiered secret management (1Password Service Accounts → SOPS+age fallback). Set up AWS Organization (4 accounts) for demos and connector testing.
- Phase 2 (when needed): Move connectors to AWS Lambda for per-customer isolation and scaling.
- Phase 3 (when revenue): Migrate platform to AWS if warranted by customer requirements.
1. Current State
Hetzner VPS (CX31/CX41, EUR 10-40/mo)
├── Docker Compose (always running)
│ ├── ui (React + nginx) :80
│ ├── api (Node/Express) :3000
│ └── mongo (MongoDB 7.0) :27017
├── Caddy (TLS termination) :443
└── Connectors: manual CLI runs (not scheduled)
├── entra-servicenow (Python)
└── azure-foundry (Python)
Domains: app.securityv0.com (prod), dev.securityv0.com (dev)
CI/CD: GitHub Actions → GHCR → docker-compose.deploy.yml
Problems:
| Problem | Impact |
|---|---|
| Connectors run manually | Cannot detect temporal drift — findings are point-in-time snapshots, not continuous monitoring |
| No secret management | Credentials in .env files on disk — no rotation, no audit trail |
| Single server | No fault isolation — a runaway connector can crash the platform |
| No AWS environment | Cannot develop/test the AWS connector against real infrastructure |
| No demo infrastructure | Cannot simulate customer environments with real cross-account trust |
Update (2026-04-01): Cloudflare Access (Zero Trust) has been enabled on both
app.securityv0.comanddev.securityv0.com. All HTTP traffic is now authenticated at the Cloudflare edge before reaching the Hetzner VPS. See Access Protection for configuration details.
2. AWS Credits: Available Funding
Confirmed Available
| Program | Credits | How to Activate | Expiration |
|---|---|---|---|
| Mercury → AWS Activate Portfolio | $5,000 | Mercury dashboard → Perks → AWS Activate. Mercury provides Org ID for Activate application. | 2 years from activation |
Additional Opportunities (Apply in Parallel)
| Program | Credits | Effort | Notes |
|---|---|---|---|
| Azure Founders Hub (self-service) | Up to $150,000 | Low — self-service application | No accelerator required. Free optionality for Azure-specific services. |
| GCP for Startups | Up to $100,000 | Medium — application review | Worth applying; GKE is best-in-class if we ever need K8s |
| AWS Startup SA outreach | $10-25K PoC credits | Medium — request meeting | Explain Hetzner → AWS migration evaluation. Separate from Activate. |
| AWS Generative AI Program | Up to $300,000 | Medium — if applicable | Requires AI/ML product angle. Potentially applicable for Bedrock demo scenarios. |
Credit Rules
- One Activate grant per account. Mercury's $5,000 is the baseline. If we later join an accelerator with a higher-tier partnership ($25K-$100K), the grant can be upgraded.
- Consolidated billing — Credits in the management account are shared across all Organization member accounts automatically.
- Credits cover most services — EC2, Lambda, ECS, Bedrock, S3, Secrets Manager, etc. Excludes Marketplace purchases, RI upfront payments, domain registration.
- Cannot stack multiple Activate grants on the same account. But Azure/GCP credits are entirely separate programs.
Credit Burn Rate Projections
| Scenario | Monthly Burn | Runway ($5K) | Description |
|---|---|---|---|
| Conservative | $70-200 | 25+ months | Connectors on Hetzner, AWS for demos only (nightly cleanup), minimal Bedrock |
| Moderate | $200-500 | 10-25 months | Regular demos, dev Bedrock usage, some always-on infrastructure |
| Aggressive | $500-1,500 | 3-10 months | Full platform migration, heavy Bedrock, multiple concurrent demos |
Recommendation: Start conservative. $5,000 at $150/mo average = 33 months. This is enough to validate the AWS strategy before committing to a migration.
3. Connector Automation
Phase 1: Hetzner + systemd timers (NOW)
The simplest path — 1-2 days to implement, zero additional cost.
Hetzner VPS
├── Docker Compose (always running)
│ ├── ui :80 → api :3000 → mongo :27017
│ │
│ └── Docker network: sv0-platform_default
│ (connectors join this network to reach API directly)
│
├── systemd timers (per connector)
│ ├── sv0-connector-entra.timer → every 30min
│ ├── sv0-connector-azure.timer → every 60min
│ └── sv0-connector-aws.timer → every 30min (future)
│
├── Connector execution:
│ docker run --rm \
│ --env-file=/etc/sv0/connectors/entra.env \
│ --network sv0-platform_default \
│ ghcr.io/securityv0/connector-entra-servicenow \
│ --all --submit --platform-url http://api:3000
│
└── Secrets: see "Secret Management" section below
Implementation steps:
- Write Dockerfiles for each connector (Python 3.11 slim, pip install, CLI entrypoint)
- Add CI workflow to build and push connector images to GHCR
- Create systemd timer + service units per connector
- Connectors join the Docker Compose network (
--network sv0-platform_default) — API reachable athttp://api:3000without public exposure - Configure secret management (see tiered options below)
- Health check: cron job queries
GET /api/v1/syncs?limit=1per connector, alerts if last sync > 2x interval
Cost: EUR 0/mo additional. Connectors use <256MB RAM each, run for 30s-5min, and exit.
Secret Management (Tiered Options)
Secret management for connectors on Hetzner has three tiers, depending on what's available. Choose the highest tier you can:
Tier 1: 1Password Business with Service Accounts (best)
Prerequisite: 1Password Business plan. Available free for 1 year via Mercury startup perks (Mercury dashboard → Perks → 1Password). The Business plan also includes free Family accounts for every team member, replacing any existing Family subscription.
Important: Service Accounts (headless, non-interactive API tokens) are a Business/Enterprise-only feature. They are NOT available on Family, Personal, or Teams plans. The Family plan supports
opCLI with interactive authentication only.
How it works:
- Create a Service Account in 1Password (web UI → Developer → Service Accounts)
- Grant it read-only access to the
sv0-connectorsvault - Store the service account token on Hetzner (one secret, root-only):
# /etc/sv0/op-token (chmod 600, owned by root)
OP_SERVICE_ACCOUNT_TOKEN=ops_eyJzaWduSW...
- Reference files contain
op://URIs — pointers, not secrets:
# /etc/sv0/connectors/entra.env (safe to store, no real credentials)
AZURE_TENANT_ID=op://sv0-connectors/entra-prod/tenant_id
AZURE_CLIENT_ID=op://sv0-connectors/entra-prod/client_id
AZURE_CLIENT_SECRET=op://sv0-connectors/entra-prod/client_secret
SERVICENOW_INSTANCE=op://sv0-connectors/servicenow-prod/instance
SERVICENOW_USER=op://sv0-connectors/servicenow-prod/username
SERVICENOW_PASSWORD=op://sv0-connectors/servicenow-prod/password
- systemd injects the token,
op runfetches secrets at runtime:
# /etc/systemd/system/sv0-connector-entra.service
[Service]
Type=oneshot
EnvironmentFile=/etc/sv0/op-token
ExecStart=/usr/local/bin/op run \
--env-file=/etc/sv0/connectors/entra.env -- \
docker run --rm --network sv0-platform_default \
ghcr.io/securityv0/connector-entra-servicenow \
--all --submit --platform-url http://api:3000
What's on disk: One service account token (gates access to everything). All actual secrets fetched over HTTPS at runtime, never written to disk.
Benefits: Centralized rotation (change secrets in 1Password web UI, no server changes), full audit trail of every secret access, one credential on disk instead of 10+.
Tier 2: SOPS + age encryption (good — no SaaS dependency)
Use when: 1Password Business is not yet activated, or you prefer no SaaS dependency for secrets.
How it works:
- Generate an age key pair:
age-keygen -o /etc/sv0/age-key.txt # chmod 600, root-only
# Public key: age1... (safe to share/commit)
- Encrypt secrets locally (on your machine):
sops --encrypt --age age1xxxxxxxxxx connectors/entra.env > connectors/entra.env.enc
# Encrypted file is safe to commit to git or store anywhere
- On Hetzner, decrypt at runtime in the systemd service:
# /etc/systemd/system/sv0-connector-entra.service
[Service]
Type=oneshot
RuntimeDirectory=sv0
RuntimeDirectoryMode=0700
ExecStartPre=/bin/sh -c 'SOPS_AGE_KEY_FILE=/etc/sv0/age-key.txt sops -d /etc/sv0/connectors/entra.env.enc > /run/sv0/entra.env'
ExecStart=docker run --rm \
--env-file=/run/sv0/entra.env \
--network sv0-platform_default \
ghcr.io/securityv0/connector-entra-servicenow \
--all --submit --platform-url http://api:3000
What's on disk: One age private key (equivalent to the 1Password service account token). Encrypted .env.enc files (useless without the key). Decrypted secrets exist only briefly in /run/ (tmpfs) during execution.
Benefits: No SaaS dependency. Encrypted secrets can live in git. Free. Works with any 1Password plan (or no 1Password at all).
Tradeoff vs Tier 1: No web UI for rotation (must re-encrypt and redeploy), no audit trail of secret access.
Tier 3: Plain .env files (simplest — acceptable for dev server)
Use when: Moving fast, Hetzner is temporary, and you accept the tradeoff.
# /etc/sv0/connectors/entra.env (chmod 600, owned by root)
AZURE_TENANT_ID=actual-tenant-id-here
AZURE_CLIENT_ID=actual-client-id-here
AZURE_CLIENT_SECRET=actual-secret-here
...
# /etc/systemd/system/sv0-connector-entra.service
[Service]
Type=oneshot
ExecStart=docker run --rm \
--env-file=/etc/sv0/connectors/entra.env \
--network sv0-platform_default \
ghcr.io/securityv0/connector-entra-servicenow \
--all --submit --platform-url http://api:3000
What's on disk: All secrets in plaintext. Root-only permissions are the only protection.
This is acceptable IF:
- The Hetzner box is a dev/staging server, not production
- You're migrating to AWS (Phase 2) where Secrets Manager replaces this
- Only 1-2 people have SSH access
- You don't need an audit trail of secret access
This is NOT acceptable when: You have customer credentials, multiple team members, or compliance requirements.
Recommendation
| Situation | Tier | Action |
|---|---|---|
| Mercury perk available | Tier 1 | Activate 1Password Business (free 1 year), set up Service Accounts |
| Business plan takes time | Tier 2 | Use SOPS+age now, migrate to Tier 1 when activated |
| Moving fast, Hetzner is temporary | Tier 3 | Plain .env files, upgrade before onboarding customers |
| Moved to AWS (Phase 2+) | AWS Secrets Manager | Replaces all of the above |
In all tiers, the systemd timer, Dockerfile, CI, and Docker network setup are identical — only the secret injection mechanism changes. You can upgrade tiers without touching the connector code.
Phase 2: Hybrid — Hetzner platform + AWS connectors (WHEN NEEDED)
Move connectors to AWS Lambda when you need per-customer isolation or >10 connectors.
Hetzner VPS AWS
├── api + ui + mongo ├── EventBridge Scheduler
│ (always running) │ (cron per connector)
│ │
│ ◄── HTTPS POST ──────────────────── ├── Lambda (container images)
│ (app.securityv0.com/api/v1/ingest) │ ├── connector-entra-servicenow
│ │ ├── connector-azure-foundry
│ │ └── connector-aws
│ │
│ ├── Secrets Manager
│ │ (per-customer credentials)
│ │
│ └── CloudWatch Logs
When to graduate:
- 10+ connectors or customer-specific connector instances
- Need per-customer secret isolation (each customer's Azure/AWS creds separate)
- Connector runs exceed 5 min or 512MB RAM
- Want CloudWatch-native monitoring and alerting
Cost estimate: Lambda path for 5 connectors every 30min = ~$6-17/mo (mostly free tier for the first year).
Phase 3: Full AWS migration (WHEN REVENUE)
Not recommended now. 4-10x cost increase for equivalent workload. Only warranted when:
- Paying customers require SLA guarantees
- Auto-scaling needed for traffic spikes
- Investor due diligence requires "production-grade" infrastructure
- Team grows beyond 2 (want deploy without SSH access)
4. AWS Organization Strategy
Why Use Organizations (Even at 2 People)
- Free — Organizations, Identity Center, SCPs cost nothing
- Consolidated billing — $5K credits shared across all accounts automatically
- Real cross-account testing — SecurityV0's product reads customer AWS accounts via AssumeRole. Single-account development can never test this.
- Budget isolation — see exactly which account is burning credits
- Blast radius — a runaway demo Lambda can't affect the platform account
Account Structure
┌──────────────────────────────────────────────────────────────┐
│ Management Account (billing + governance only) │
│ ├── AWS Budgets, Cost Explorer, Cost Anomaly Detection │
│ ├── IAM Identity Center (SSO for Ivan + CEO) │
│ ├── Service Control Policies │
│ └── Credits applied here → shared via consolidated billing │
│ │
│ OU: Platform │
│ ├── sv0-platform │
│ │ SecurityV0 SaaS infrastructure (when/if migrated) │
│ │ Currently: placeholder for future use │
│ │ │
│ OU: Sandbox │
│ ├── sv0-demo │
│ │ Ephemeral customer simulations │
│ │ Lambda, Step Functions, Bedrock, IAM roles │
│ │ Nightly cleanup via cloud-nuke │
│ │ Budget cap: $200/mo with auto-freeze SCP │
│ │ │
│ OU: Security │
│ └── sv0-security-tooling │
│ Simulates a real customer AWS environment │
│ Cross-account trust role → Platform account │
│ Persistent (not cleaned nightly) │
│ Used for: connector dev/test, integration testing │
└──────────────────────────────────────────────────────────────┘
Account Purposes
| Account | Purpose | Ephemeral? | Budget |
|---|---|---|---|
| Management | Billing, SCPs, Identity Center. No workloads ever. | No | $0 (governance only) |
| sv0-platform | SecurityV0 SaaS infrastructure (future). Placeholder now. | No | $300/mo cap |
| sv0-demo | Demo customer environments. Lambda, Bedrock, IAM, Step Functions. CDK-deployed, nightly cleanup. | Yes | $200/mo cap + auto-freeze |
| sv0-security-tooling | Persistent customer simulation. SecurityV0 connector reads from this account via cross-account role. Tests real AssumeRole flow. | No | $50/mo cap |
IAM Identity Center (SSO)
Free, eliminates IAM user management:
| User | Management Account | sv0-platform | sv0-demo | sv0-security-tooling |
|---|---|---|---|---|
| Ivan (CTO) | BillingAccess | AdministratorAccess | AdministratorAccess | AdministratorAccess |
| CEO | BillingAccess | ReadOnlyAccess | — | — |
Local credential management: granted.dev (open-source, fuzzy-find across SSO profiles):
brew install common-fate/granted/granted
assume sv0-demo-admin # Interactive or direct — sets creds in current shell
5. Budget Protection
Service Control Policies (Apply Immediately)
SCP 1: Region Lock (entire Organization)
Only allow us-east-1 and us-west-2. Prevents resources in forgotten regions.
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyNonApprovedRegions",
"Effect": "Deny",
"NotAction": [
"iam:*", "sts:*", "organizations:*",
"budgets:*", "ce:*", "support:*",
"health:*", "billing:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": ["us-east-1", "us-west-2"]
}
}
}]
}
SCP 2: Block Expensive Services (Sandbox OU)
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyExpensiveServices",
"Effect": "Deny",
"Action": [
"sagemaker:CreateTrainingJob",
"sagemaker:CreateEndpoint",
"redshift:CreateCluster",
"opensearch:CreateDomain",
"elasticache:CreateCacheCluster",
"emr:RunJobFlow",
"eks:CreateCluster",
"glue:CreateJob",
"kinesis:CreateStream",
"kafka:CreateCluster"
],
"Resource": "*"
}]
}
SCP 3: Instance Size Cap (Sandbox OU)
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyLargeEC2",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotLike": {
"ec2:InstanceType": [
"t3.micro", "t3.small", "t3.medium",
"t4g.micro", "t4g.small", "t4g.medium"
]
}
}
}]
}
SCP 4: Bedrock Model Restriction (Sandbox OU)
Only allow cheap models in demo environments:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "RestrictBedrockModels",
"Effect": "Deny",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": "*",
"Condition": {
"ForAnyValue:StringNotEquals": {
"bedrock:ModelId": [
"anthropic.claude-3-haiku-20240307-v1:0",
"amazon.titan-text-lite-v1"
]
}
}
}]
}
SCP 5: Budget Freeze (Applied automatically by AWS Budget Actions)
When demo account hits $200/mo, this SCP is attached automatically:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "FreezeAccount",
"Effect": "Deny",
"Action": "*",
"Resource": "*"
}]
}
AWS Budgets
| Budget | Scope | Amount | Alerts | Action at 100% |
|---|---|---|---|---|
| Total | Entire org | $500/mo | 50%, 80%, 100% email | Notify only |
| Demo | sv0-demo account | $200/mo | 80% email | Auto-apply freeze SCP |
| Platform | sv0-platform account | $300/mo | 80%, 100% email | Notify only |
Cost Allocation Tags
Define organization-wide, enforce via SCP:
sv0:environment = prod | dev | demo | test
sv0:project = platform | demo-acme | demo-globex | connector-test
sv0:owner = ivan | automated | demo-script
sv0:ephemeral = true | false
sv0:ttl = 2h | 4h | 24h | permanent
6. Demo Lab Environments
Strategy
Use AWS CDK v2 (TypeScript) for demo infrastructure. Each demo scenario is a CDK stack that can be deployed and destroyed cleanly.
sv0-platform/
infra/
cdk/
bin/demo-app.ts
lib/
demo-customer-stack.ts # Cross-account trust, IAM, Lambda
demo-bedrock-stack.ts # Bedrock agents, knowledge bases
demo-serverless-stack.ts # Step Functions, EventBridge, Secrets
scenarios/
cross-account-trust.yaml # Scenario definition
bedrock-support-agent.yaml
github-to-runtime.yaml
CDK Demo Stack Example
export class DemoCustomerStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: DemoProps) {
super(scope, id, props);
// Cross-account trust role (simulates customer's SecurityV0 onboarding)
new iam.Role(this, 'SecurityV0ReadRole', {
roleName: 'SecurityV0-ReadOnly',
assumedBy: new iam.AccountPrincipal(props.platformAccountId),
externalIds: [props.externalId],
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('SecurityAudit'),
],
});
// Sample Lambda (discoverable workload)
const fn = new lambda.Function(this, 'ClaimsProcessor', { ... });
// Sample Step Functions (cross-account orchestration)
const stateMachine = new sfn.StateMachine(this, 'ClaimsWorkflow', { ... });
// Tag everything as ephemeral
cdk.Tags.of(this).add('sv0:ephemeral', 'true');
cdk.Tags.of(this).add('sv0:ttl', '24');
}
}
Nightly Cleanup
EventBridge rule (2 AM UTC) → Lambda → cloud-nuke on sv0-demo account:
EventBridge: cron(0 2 * * ? *)
→ Lambda (in management or security-tooling account)
→ Assume role into sv0-demo
→ cloud-nuke: delete resources older than 12 hours
→ SNS notification with cleanup summary
Alternative: cdk destroy for known stacks if using named stacks.
Free Tier Optimization for Demos
Most demo components are within the free tier if torn down nightly:
| Service | Free Tier | Demo Usage |
|---|---|---|
| Lambda | 1M requests + 400K GB-seconds/mo | Easily within |
| Step Functions | 4,000 state transitions/mo | Easily within |
| DynamoDB | 25 GB + 25 WCU/RCU | Sample data fits |
| CloudTrail | 1 trail free/account (mgmt events) | Sufficient |
| IAM, STS, Organizations, Identity Center | Always free | Core to demos |
| EventBridge | All events free | Scheduling free |
Biggest cost risk: Bedrock model invocations. Mitigated by SCP restricting to Haiku/Titan Lite only.
7. Tools
NOW (Phase 1)
| Tool | Purpose | Cost |
|---|---|---|
| 1Password Business | Secret management with Service Accounts for headless connector runs. Free 1 year via Mercury perks. Includes Family accounts for every team member. | Free 1yr (Mercury), then $7.99/user/mo |
| SOPS + age (fallback) | Encrypt secrets in git, decrypt at runtime. Use if 1Password Business not yet activated. | Free |
| systemd timers | Connector scheduling on Hetzner | Free (built into Linux) |
| granted.dev | AWS SSO credential management locally | Free, open source |
| cloud-nuke | Nightly cleanup of demo account | Free, open source |
| AWS CDK v2 | Demo infrastructure as code | Free |
LATER
| Tool | Purpose | When |
|---|---|---|
| Temporal.io | Workflow orchestration for complex connector dependencies | 15+ connectors with inter-dependencies |
| org-formation | AWS Organization as code | 8+ accounts |
| AWS Control Tower | Landing zone management | 10+ accounts, customer onboarding |
| Infracost | Cost estimation in CI/CD | Multiple CDK stacks in regular use |
| Terraform | Multi-account infrastructure | When CDK becomes insufficient |
8. Phased Implementation Plan
Phase 1: Foundation (Week 1-2, ~$0/mo)
Day 1: Credits + Organization + 1Password
- Activate Mercury → AWS Activate ($5,000 credits)
- Activate Mercury → 1Password Business (free 1 year) — unlocks Service Accounts for headless connector runs. Replaces existing Family plan (Business includes Family for every team member).
- Apply to Azure Founders Hub + GCP for Startups (parallel, free)
- Create AWS Organization (management account)
- Create 3 member accounts (sv0-platform, sv0-demo, sv0-security-tooling)
- Enable IAM Identity Center, create users + permission sets
- Install granted.dev locally
Day 2: Budget Protection
- Apply SCPs: region lock, expensive service block, instance size cap, Bedrock model restriction
- Create AWS Budgets with alerts + auto-freeze on demo account
- Enable Cost Anomaly Detection
- Activate cost allocation tags
Day 3-4: Connector Automation
- Write Dockerfiles for entra-servicenow and azure-foundry connectors
- Add CI workflow to build and push connector images to GHCR
- Create systemd timer + service units on Hetzner
- Configure secret management on Hetzner:
- If 1Password Business activated: create Service Account, set up
op runwithop://reference files (Tier 1) - If not yet activated: use SOPS+age encrypted
.envfiles (Tier 2), or plain.envfiles as stopgap (Tier 3)
- If 1Password Business activated: create Service Account, set up
- Test automated connector runs (verify syncs appear in platform)
Day 5: Verification
- Verify temporal findings now work (multiple connector runs → drift detection)
- Verify budget alerts fire (create a small test resource, check email)
- Verify SSO login works from local machine via granted.dev
Phase 2: Demo Infrastructure (Week 3-4, ~$10-50/mo)
- Create CDK project for demo customer environments
- Deploy first demo scenario (cross-account serverless) in sv0-demo
- Create cross-account trust role in sv0-security-tooling
- Test SecurityV0 connector against sv0-security-tooling (real AssumeRole flow)
- Set up nightly cloud-nuke Lambda for sv0-demo
- Validate nightly cleanup works and sends notification
Phase 3: Connector Migration to AWS (When Needed)
Triggers: 10+ connectors, per-customer isolation needed, or connector runs >5min.
- Move connectors to AWS Lambda (container images)
- EventBridge Scheduler for cron rules
- AWS Secrets Manager for per-customer credentials
- CloudWatch for monitoring, alerting
Phase 4: Platform Migration (When Revenue)
Triggers: paying customers, SLA requirements, team >2 people.
- ECS Fargate for API + UI
- MongoDB Atlas M10 for database
- ALB with ACM TLS
- Full CI/CD to ECS
- Decommission Hetzner
9. Cost Comparison
Current State vs Phased Options
| Component | Current (Hetzner) | Phase 1 (Hetzner + AWS Org) | Phase 2 (+Demos) | Phase 3 (+Lambda) | Phase 4 (Full AWS) |
|---|---|---|---|---|---|
| Platform hosting | EUR 15-40/mo | EUR 15-40/mo | EUR 15-40/mo | EUR 15-40/mo | $150-175/mo |
| Connector automation | $0 (manual) | $0 (systemd) | $0 (systemd) | $6-17/mo | $6-17/mo |
| Secret management | $0 (.env files) | $0 (1Password Business free via Mercury, or SOPS+age) | $0 (same) | $4/mo (Secrets Mgr) | $4/mo |
| AWS Organization | — | $0 | $0 | $0 | $0 |
| Demo infrastructure | — | — | $10-50/mo | $10-50/mo | $10-50/mo |
| Database | included | included | included | included | $57/mo (Atlas) |
| Monitoring | DIY | DIY | DIY + CloudWatch | CloudWatch | CloudWatch |
| Total | EUR 15-40/mo | EUR 15-40/mo | EUR 25-90/mo | $50-150/mo | $230-310/mo |
Credit Runway at Each Phase
| Phase | Monthly Burn (AWS only) | Runway ($5K credits) |
|---|---|---|
| Phase 1 | ~$0 | Infinite (no AWS usage) |
| Phase 2 | $10-50 | 100+ months (only demos) |
| Phase 3 | $20-70 | 70+ months |
| Phase 4 | $230-310 | 16-22 months |
10. Decisions
Decided
| Decision | Choice | Rationale |
|---|---|---|
| Activate AWS credits | Yes, via Mercury ($5K) | Free money, no downside |
| Use AWS Organizations | Yes, 4 accounts from day one | Free, enables real cross-account testing |
| Connector automation (now) | systemd timers + 1Password on Hetzner | Simplest, zero cost, 1-2 days |
| Secret management (now) | Tiered: 1Password Business Service Accounts (best) → SOPS+age (fallback) → plain .env (stopgap) | 1Password Business free via Mercury perks; SOPS+age is free with no SaaS dependency; plain .env acceptable for temporary dev server |
| Demo infrastructure | CDK v2 (TypeScript) | Matches stack, clean deploy/destroy |
| Demo cleanup | cloud-nuke via nightly Lambda | Simple, proven |
| Budget protection | SCPs + AWS Budgets with auto-freeze | Prevents surprise bills |
| Local AWS creds | granted.dev + Identity Center | Free, best DX |
Deferred
| Decision | Defer Until | Current Alternative |
|---|---|---|
| Full AWS platform migration | Paying customers or SLA requirement | Stay on Hetzner |
| AWS Lambda for connectors | 10+ connectors or per-customer isolation | systemd timers |
| Temporal.io for orchestration | Complex connector dependencies | Simple cron scheduling |
| Control Tower | 10+ accounts | Manual org setup |
| Terraform for multi-account | CDK proves insufficient | CDK + AWS CLI |
| MongoDB Atlas / DocumentDB | Platform migration | Self-hosted MongoDB on Hetzner |