From 9612af4c7a860ab42c257dc7ae994b449d3c567d Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Wed, 11 Mar 2026 00:57:32 -0300 Subject: [PATCH 1/7] chore: mark story 3.2 in-progress, add story spec --- _bmad-output/epic-execution-state.yaml | 6 +- .../3-2-create-fly-deployment-template.md | 180 ++++++++++++++++++ .../sprint-status.yaml | 2 +- 3 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 _bmad-output/implementation-artifacts/3-2-create-fly-deployment-template.md diff --git a/_bmad-output/epic-execution-state.yaml b/_bmad-output/epic-execution-state.yaml index 9205d76d..891c404a 100644 --- a/_bmad-output/epic-execution-state.yaml +++ b/_bmad-output/epic-execution-state.yaml @@ -11,9 +11,9 @@ stories: dependsOn: [] - id: "3.2" title: "Create Fly deployment template" - status: pending - currentPhase: "" - branch: "" + status: in-progress + currentPhase: "setup" + branch: "feat/3.2-fly-deployment-template" pr: null dependsOn: [] skippedIssues: [] diff --git a/_bmad-output/implementation-artifacts/3-2-create-fly-deployment-template.md b/_bmad-output/implementation-artifacts/3-2-create-fly-deployment-template.md new file mode 100644 index 00000000..035fca57 --- /dev/null +++ b/_bmad-output/implementation-artifacts/3-2-create-fly-deployment-template.md @@ -0,0 +1,180 @@ +# Story 3.2: Create Fly deployment template + +Status: ready-for-dev + +## Story + +As a developer, +I want to deploy zopp-server on Fly using a provided template, +so that my team has a shared server without managing infrastructure. + +## Acceptance Criteria + +1. **Given** the deployment template exists at `deploy/fly/fly.toml`, **When** the user runs `fly launch` with the template, **Then** Fly provisions the app with the correct configuration. + +2. **Given** the template configuration, **When** the server starts, **Then** PostgreSQL is configured as the database backend (not SQLite) **And** TLS is handled automatically by Fly's platform **And** a health check endpoint is configured at `/healthz` on port 8080. + +3. **Given** the server is deployed and running, **When** the user runs `fly ssh console -C "zopp-server invite create"`, **Then** an invite token is generated that can be shared with team members. + +4. **Given** the `deploy/fly/README.md` exists, **When** a user reads it, **Then** it contains step-by-step deployment instructions **And** it documents required and optional environment variables **And** it explains how to generate the first invite token **And** it explains how to connect the CLI to the deployed server. + +## Tasks / Subtasks + +- [ ] Task 1: Create Fly deployment template (AC: 1, 2) + - [ ] 1.1 Create `deploy/fly/fly.toml` with app configuration + - [ ] 1.2 Configure PostgreSQL as attached database (Fly Postgres) + - [ ] 1.3 Configure health check using `/healthz` on port 8080 + - [ ] 1.4 Configure gRPC service on internal port 50051 + - [ ] 1.5 Set appropriate VM size and auto-stop settings + +- [ ] Task 2: Create deployment README (AC: 3, 4) + - [ ] 2.1 Create `deploy/fly/README.md` with step-by-step instructions + - [ ] 2.2 Document `fly launch` and `fly deploy` workflow + - [ ] 2.3 Document Fly Postgres setup (`fly postgres create` + `fly postgres attach`) + - [ ] 2.4 Document environment variables (DATABASE_URL, email config, etc.) + - [ ] 2.5 Document invite token generation via `fly ssh console` + - [ ] 2.6 Document connecting CLI to deployed server + - [ ] 2.7 Document TLS (automatic via Fly's edge, gRPC uses h2c internally) + +- [ ] Task 3: Validation + - [ ] 3.1 Verify fly.toml passes `fly launch --config deploy/fly/fly.toml` syntax check (dry review) + - [ ] 3.2 Ensure README instructions are consistent with fly.toml settings + +## Dev Notes + +### Fly Platform Details + +**Fly Machines Configuration:** +- Fly uses `fly.toml` for app configuration +- Apps are deployed as Machines (micro VMs) +- Fly handles TLS termination at the edge — the app runs plain gRPC (h2c) internally +- Fly Postgres is a managed Postgres service attached to apps + +**Key fly.toml Sections:** +```toml +app = "zopp-server" # User will change this during fly launch + +[build] + dockerfile = "server.Dockerfile" + +[env] + # Non-secret environment variables + ZOPP_EMAIL_VERIFICATION_REQUIRED = "false" + +[http_service] + internal_port = 50051 + force_https = true + auto_stop_machines = "stop" + auto_start_machines = true + min_machines_running = 0 + processes = ["app"] + +[[services]] + protocol = "tcp" + internal_port = 50051 + + [[services.ports]] + port = 443 + handlers = ["tls", "http"] + + [[services.ports]] + port = 80 + handlers = ["http"] + +[checks] + [checks.health] + port = 8080 + type = "http" + interval = "10s" + timeout = "2s" + path = "/healthz" + method = "GET" + +[[vm]] + size = "shared-cpu-1x" + memory = "256mb" +``` + +**Database Configuration:** +- Users create a Fly Postgres cluster: `fly postgres create --name zopp-db` +- Attach to app: `fly postgres attach zopp-db --app zopp-server` +- This automatically sets `DATABASE_URL` as a secret on the app +- zopp-server detects the postgres:// URL and uses PostgreSQL backend + +**Health Checks:** +- zopp-server serves HTTP health on port 8080 by default (`--health-addr 0.0.0.0:8080`) +- Liveness: `GET /healthz` → 200 "ok" +- Readiness: `GET /readyz` → 200 when gRPC ready, 503 otherwise +- Fly uses checks to determine if a Machine is healthy + +**TLS:** +- Fly automatically terminates TLS at the edge +- Internal traffic within Fly's network is h2c (HTTP/2 cleartext) +- No need for `--tls-cert` / `--tls-key` flags on the server +- CLI connects to `https://.fly.dev:443` — Fly routes to internal port 50051 + +**Invite Token Generation:** +- Users run: `fly ssh console -C "/usr/local/bin/zopp-server invite create"` +- The zopp-server binary is at `/usr/local/bin/zopp-server` in the Docker image +- This outputs an invite token that can be shared +- Note: The `invite create` subcommand on the server binary creates a bootstrap invite + +**CLI Connection:** +- After deployment, the server is available at `https://.fly.dev` +- Users configure their CLI: `zopp join --server https://.fly.dev` + +### Environment Variables Reference + +**Required (set automatically by Fly Postgres attach):** +- `DATABASE_URL` — PostgreSQL connection string + +**Optional:** +- `ZOPP_EMAIL_VERIFICATION_REQUIRED` — "true"/"false" (default "true") +- `ZOPP_EMAIL_PROVIDER` — "resend" or "smtp" +- `ZOPP_EMAIL_FROM` — Sender email address +- `RESEND_API_KEY` — If using Resend email provider +- SMTP variables if using SMTP + +### Architecture Notes + +- This story is purely configuration/documentation — no Rust code changes +- The existing `server.Dockerfile` is used as-is (Fly builds from Dockerfile) +- The `deploy/` directory is new; this is the first deployment template +- Follow similar patterns to the Helm chart in `charts/zopp/` for reference on configuration values + +### Project Structure Notes + +- New directory: `deploy/fly/` +- Files: `deploy/fly/fly.toml`, `deploy/fly/README.md` +- No changes to existing source code +- No Cargo.toml changes + +### References + +- [Source: server.Dockerfile] — Docker build for zopp-server +- [Source: apps/zopp-server/src/main.rs] — Server startup, health check endpoints, CLI args +- [Source: charts/zopp/values.yaml] — Helm chart defaults (reference for env vars) +- [Source: CLAUDE.md] — Server commands, TLS configuration, DATABASE_URL usage + +### Pre-Submission Checklist + +Before submitting a PR, verify each item relevant to your story's scope. + +**Documentation:** + +- [ ] fly.toml uses correct ports (50051 gRPC, 8080 health) +- [ ] README instructions are accurate and complete +- [ ] No secrets or credentials appear in template files +- [ ] Environment variable documentation matches server implementation + +## Dev Agent Record + +### Agent Model Used + +Claude Opus 4.6 + +### Debug Log References + +### Completion Notes List + +### File List diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index e869cfe5..eb21346d 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -58,7 +58,7 @@ development_status: # Epic 3: Fly Integration (Sync & Deployment) (Phase 1) epic-3: in-progress 3-1-implement-fly-sync-target-and-cli-commands: done - 3-2-create-fly-deployment-template: backlog + 3-2-create-fly-deployment-template: in-progress epic-3-retrospective: optional # Epic 4: GCP Secret Manager Integration (Phase 2) From d95eac144aedec1c10581e3a783a03054ab25b15 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Wed, 11 Mar 2026 00:59:07 -0300 Subject: [PATCH 2/7] feat: add Fly deployment template and instructions --- deploy/fly/README.md | 154 +++++++++++++++++++++++++++++++++++++++++++ deploy/fly/fly.toml | 47 +++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 deploy/fly/README.md create mode 100644 deploy/fly/fly.toml diff --git a/deploy/fly/README.md b/deploy/fly/README.md new file mode 100644 index 00000000..95c79bf3 --- /dev/null +++ b/deploy/fly/README.md @@ -0,0 +1,154 @@ +# Deploy zopp-server on Fly + +Deploy a zopp-server instance on [Fly.io](https://fly.io) with PostgreSQL. + +## Prerequisites + +- [Fly CLI](https://fly.io/docs/flyctl/install/) installed and authenticated (`fly auth login`) +- A Fly account with billing configured + +## Quick Start + +### 1. Launch the app + +From the repository root: + +```bash +fly launch --config deploy/fly/fly.toml --no-deploy +``` + +Fly will prompt you to set the app name and region. Choose a region close to your team. + +### 2. Create a PostgreSQL database + +```bash +fly postgres create --name zopp-db +``` + +Choose the same region as your app. For development, the "Development" plan is sufficient. + +### 3. Attach the database + +```bash +fly postgres attach zopp-db --app +``` + +This automatically sets the `DATABASE_URL` secret on your app. + +### 4. Deploy + +```bash +fly deploy --config deploy/fly/fly.toml +``` + +The first deploy takes a few minutes to build the Docker image. + +### 5. Verify the deployment + +```bash +fly status --app +``` + +Check that the Machine is running and healthy. + +## Generate an Invite Token + +After deploying, create the first invite token to bootstrap your workspace: + +```bash +fly ssh console --app -C "/usr/local/bin/zopp-server invite create" +``` + +This outputs an invite token. Save it securely — you'll use it to join the server from the CLI. + +## Connect the CLI + +With the invite token from the previous step: + +```bash +zopp join --server https://.fly.dev +``` + +This registers your device as a principal on the server. You can then create workspaces and manage secrets: + +```bash +zopp workspace create my-workspace +zopp project create my-project +zopp environment create production +zopp secret set API_KEY "my-secret-value" +``` + +## Environment Variables + +### Set automatically + +| Variable | Description | +|---|---| +| `DATABASE_URL` | PostgreSQL connection string (set by `fly postgres attach`) | + +### Configured in fly.toml + +| Variable | Default | Description | +|---|---|---| +| `ZOPP_EMAIL_VERIFICATION_REQUIRED` | `false` | Set to `true` once email is configured | + +### Optional (set as Fly secrets) + +Set these with `fly secrets set`: + +```bash +# Enable email verification with Resend +fly secrets set \ + ZOPP_EMAIL_VERIFICATION_REQUIRED=true \ + ZOPP_EMAIL_PROVIDER=resend \ + ZOPP_EMAIL_FROM=noreply@yourdomain.com \ + RESEND_API_KEY=re_xxxxx + +# Or use SMTP +fly secrets set \ + ZOPP_EMAIL_VERIFICATION_REQUIRED=true \ + ZOPP_EMAIL_PROVIDER=smtp \ + ZOPP_EMAIL_FROM=noreply@yourdomain.com \ + SMTP_HOST=smtp.example.com \ + SMTP_PORT=587 \ + SMTP_USERNAME=user \ + SMTP_PASSWORD=pass +``` + +## TLS + +Fly automatically terminates TLS at the edge. The zopp-server runs plain gRPC internally — no TLS certificate configuration is needed. + +Your CLI connects via `https://.fly.dev` and Fly handles the TLS termination. + +## Scaling + +The default configuration uses a shared CPU with 256MB RAM and auto-stop enabled. To adjust: + +```bash +# Scale VM size +fly scale vm shared-cpu-2x --memory 512 --app + +# Keep at least one Machine running (disable auto-stop) +fly scale count 1 --app +``` + +## Troubleshooting + +### Check server logs + +```bash +fly logs --app +``` + +### Check health + +```bash +curl https://.fly.dev/healthz +``` + +### SSH into the Machine + +```bash +fly ssh console --app +``` diff --git a/deploy/fly/fly.toml b/deploy/fly/fly.toml new file mode 100644 index 00000000..76d62515 --- /dev/null +++ b/deploy/fly/fly.toml @@ -0,0 +1,47 @@ +# Fly deployment configuration for zopp-server +# +# Usage: +# fly launch --config deploy/fly/fly.toml +# fly deploy --config deploy/fly/fly.toml +# +# See deploy/fly/README.md for full deployment instructions. + +app = "zopp-server" +primary_region = "iad" + +[build] + dockerfile = "server.Dockerfile" + +[env] + # Disable email verification for initial setup (enable once email is configured) + ZOPP_EMAIL_VERIFICATION_REQUIRED = "false" + +# gRPC service exposed on port 443 (TLS terminated by Fly) +[[services]] + protocol = "tcp" + internal_port = 50051 + auto_stop_machines = "stop" + auto_start_machines = true + min_machines_running = 0 + + [[services.ports]] + port = 443 + handlers = ["tls"] + + [[services.ports]] + port = 80 + handlers = ["http"] + +# Health checks using the HTTP health endpoint +[checks] + [checks.health] + port = 8080 + type = "http" + interval = "10s" + timeout = "2s" + path = "/healthz" + method = "GET" + +[[vm]] + size = "shared-cpu-1x" + memory = "256mb" From ec0b108c498ba5bf8b0601bcfaa67f0112b8bd43 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Wed, 11 Mar 2026 00:59:11 -0300 Subject: [PATCH 3/7] chore: update story 3.2 tracking files --- _bmad-output/epic-execution-state.yaml | 2 +- .../3-2-create-fly-deployment-template.md | 49 +++++++++++-------- .../sprint-status.yaml | 2 +- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/_bmad-output/epic-execution-state.yaml b/_bmad-output/epic-execution-state.yaml index 891c404a..27febc55 100644 --- a/_bmad-output/epic-execution-state.yaml +++ b/_bmad-output/epic-execution-state.yaml @@ -12,7 +12,7 @@ stories: - id: "3.2" title: "Create Fly deployment template" status: in-progress - currentPhase: "setup" + currentPhase: "pr-ci" branch: "feat/3.2-fly-deployment-template" pr: null dependsOn: [] diff --git a/_bmad-output/implementation-artifacts/3-2-create-fly-deployment-template.md b/_bmad-output/implementation-artifacts/3-2-create-fly-deployment-template.md index 035fca57..e4f853b3 100644 --- a/_bmad-output/implementation-artifacts/3-2-create-fly-deployment-template.md +++ b/_bmad-output/implementation-artifacts/3-2-create-fly-deployment-template.md @@ -1,6 +1,6 @@ # Story 3.2: Create Fly deployment template -Status: ready-for-dev +Status: review ## Story @@ -20,25 +20,25 @@ so that my team has a shared server without managing infrastructure. ## Tasks / Subtasks -- [ ] Task 1: Create Fly deployment template (AC: 1, 2) - - [ ] 1.1 Create `deploy/fly/fly.toml` with app configuration - - [ ] 1.2 Configure PostgreSQL as attached database (Fly Postgres) - - [ ] 1.3 Configure health check using `/healthz` on port 8080 - - [ ] 1.4 Configure gRPC service on internal port 50051 - - [ ] 1.5 Set appropriate VM size and auto-stop settings - -- [ ] Task 2: Create deployment README (AC: 3, 4) - - [ ] 2.1 Create `deploy/fly/README.md` with step-by-step instructions - - [ ] 2.2 Document `fly launch` and `fly deploy` workflow - - [ ] 2.3 Document Fly Postgres setup (`fly postgres create` + `fly postgres attach`) - - [ ] 2.4 Document environment variables (DATABASE_URL, email config, etc.) - - [ ] 2.5 Document invite token generation via `fly ssh console` - - [ ] 2.6 Document connecting CLI to deployed server - - [ ] 2.7 Document TLS (automatic via Fly's edge, gRPC uses h2c internally) - -- [ ] Task 3: Validation - - [ ] 3.1 Verify fly.toml passes `fly launch --config deploy/fly/fly.toml` syntax check (dry review) - - [ ] 3.2 Ensure README instructions are consistent with fly.toml settings +- [x] Task 1: Create Fly deployment template (AC: 1, 2) + - [x] 1.1 Create `deploy/fly/fly.toml` with app configuration + - [x] 1.2 Configure PostgreSQL as attached database (Fly Postgres) + - [x] 1.3 Configure health check using `/healthz` on port 8080 + - [x] 1.4 Configure gRPC service on internal port 50051 + - [x] 1.5 Set appropriate VM size and auto-stop settings + +- [x] Task 2: Create deployment README (AC: 3, 4) + - [x] 2.1 Create `deploy/fly/README.md` with step-by-step instructions + - [x] 2.2 Document `fly launch` and `fly deploy` workflow + - [x] 2.3 Document Fly Postgres setup (`fly postgres create` + `fly postgres attach`) + - [x] 2.4 Document environment variables (DATABASE_URL, email config, etc.) + - [x] 2.5 Document invite token generation via `fly ssh console` + - [x] 2.6 Document connecting CLI to deployed server + - [x] 2.7 Document TLS (automatic via Fly's edge, gRPC uses h2c internally) + +- [x] Task 3: Validation + - [x] 3.1 Verify fly.toml passes `fly launch --config deploy/fly/fly.toml` syntax check (dry review) + - [x] 3.2 Ensure README instructions are consistent with fly.toml settings ## Dev Notes @@ -177,4 +177,13 @@ Claude Opus 4.6 ### Completion Notes List +- Created fly.toml with gRPC on port 50051, health checks on port 8080 +- PostgreSQL via Fly Postgres attach (sets DATABASE_URL automatically) +- TLS handled by Fly edge — no server-side TLS config needed +- README covers full deployment workflow: launch, postgres, deploy, invite, connect CLI +- Email verification disabled by default in template (enable after configuring email provider) + ### File List + +- deploy/fly/fly.toml (new) — Fly deployment configuration +- deploy/fly/README.md (new) — Step-by-step deployment instructions diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index eb21346d..0e79f650 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -58,7 +58,7 @@ development_status: # Epic 3: Fly Integration (Sync & Deployment) (Phase 1) epic-3: in-progress 3-1-implement-fly-sync-target-and-cli-commands: done - 3-2-create-fly-deployment-template: in-progress + 3-2-create-fly-deployment-template: review epic-3-retrospective: optional # Epic 4: GCP Secret Manager Integration (Phase 2) From 7b5131e7b5aeec780cfd2486a0080689825e8598 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Wed, 11 Mar 2026 01:05:00 -0300 Subject: [PATCH 4/7] fix: use fly checks list for health check troubleshooting --- deploy/fly/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/fly/README.md b/deploy/fly/README.md index 95c79bf3..fe484e22 100644 --- a/deploy/fly/README.md +++ b/deploy/fly/README.md @@ -144,7 +144,7 @@ fly logs --app ### Check health ```bash -curl https://.fly.dev/healthz +fly checks list --app ``` ### SSH into the Machine From a2280c542751501d7d0ad79d92c19eaf0da92650 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Wed, 11 Mar 2026 01:13:19 -0300 Subject: [PATCH 5/7] fix: correct --server flag position in zopp join example --- deploy/fly/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/fly/README.md b/deploy/fly/README.md index fe484e22..33089fe3 100644 --- a/deploy/fly/README.md +++ b/deploy/fly/README.md @@ -66,7 +66,7 @@ This outputs an invite token. Save it securely — you'll use it to join the ser With the invite token from the previous step: ```bash -zopp join --server https://.fly.dev +zopp --server https://.fly.dev join ``` This registers your device as a principal on the server. You can then create workspaces and manage secrets: From 7e1280c98d7a58c296833ba145e8be578204fc21 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Wed, 11 Mar 2026 01:15:52 -0300 Subject: [PATCH 6/7] chore: mark story 3.2 as completed --- _bmad-output/epic-execution-state.yaml | 6 +++--- _bmad-output/implementation-artifacts/sprint-status.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/_bmad-output/epic-execution-state.yaml b/_bmad-output/epic-execution-state.yaml index 27febc55..cc751b93 100644 --- a/_bmad-output/epic-execution-state.yaml +++ b/_bmad-output/epic-execution-state.yaml @@ -11,9 +11,9 @@ stories: dependsOn: [] - id: "3.2" title: "Create Fly deployment template" - status: in-progress - currentPhase: "pr-ci" + status: completed + currentPhase: "" branch: "feat/3.2-fly-deployment-template" - pr: null + pr: 87 dependsOn: [] skippedIssues: [] diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 0e79f650..9fbb2915 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -58,7 +58,7 @@ development_status: # Epic 3: Fly Integration (Sync & Deployment) (Phase 1) epic-3: in-progress 3-1-implement-fly-sync-target-and-cli-commands: done - 3-2-create-fly-deployment-template: review + 3-2-create-fly-deployment-template: done epic-3-retrospective: optional # Epic 4: GCP Secret Manager Integration (Phase 2) From 8bb47e36b64b449ea255d46e50696e52632be57a Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Wed, 11 Mar 2026 01:16:28 -0300 Subject: [PATCH 7/7] chore: mark epic 3 as complete --- _bmad-output/epic-execution-state.yaml | 5 +++++ _bmad-output/implementation-artifacts/sprint-status.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/_bmad-output/epic-execution-state.yaml b/_bmad-output/epic-execution-state.yaml index cc751b93..1a0a4e25 100644 --- a/_bmad-output/epic-execution-state.yaml +++ b/_bmad-output/epic-execution-state.yaml @@ -17,3 +17,8 @@ stories: pr: 87 dependsOn: [] skippedIssues: [] +completedAt: "2026-03-10" +summary: + total: 2 + completed: 2 + skipped: 0 diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 9fbb2915..4b5950c7 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -56,7 +56,7 @@ development_status: epic-2-retrospective: done # Epic 3: Fly Integration (Sync & Deployment) (Phase 1) - epic-3: in-progress + epic-3: done 3-1-implement-fly-sync-target-and-cli-commands: done 3-2-create-fly-deployment-template: done epic-3-retrospective: optional