# ADR-060: Production Promotion Uses rsync Allowlist — Not Full Git Checkout

## Status

Accepted, 2026-06-22.

## Status History

```yaml
status_history:
  - date: 2026-06-22
    status: Proposed
    changed_by: hkl
    reason: Formalising rsync allowlist deploy strategy from deploy-prod.sh
    changed_via: adr-kit (360lm)
  - date: 2026-06-22
    status: Accepted
    changed_by: hkl
    reason: deploy-prod.sh using rsync allowlist since prod stack exists; MVP PWA list stable
    changed_via: adr-kit (360lm)
```

## Context

360lm has two stacks on the same VPS: dev (dev.srv1111289) and prod (srv1111289) (ADR-015). The master branch contains all PWAs — including in-progress features, experimental PWAs, and staging-only tools. A full git checkout of master into the prod web root would expose all PWAs, including unfinished ones, to production users. The platform requires a controlled subset ("MVP PWAs") to be promoted to production while other PWAs remain dev-only.

## Decision

`deploy-prod.sh` promotes to production via these steps:
1. `git pull origin master` — updates dev tree.
2. `rsync` named MVP PWA directories from dev web root to prod web root (explicit allowlist, not wildcard).
3. Apply pending SQL migrations.
4. Reload PostgREST (`docker exec … kill -HUP`).

**Current MVP PWA allowlist:** Admin, Hub, Custodian, Activity, Recce, Recce-Client, Shared (and their `sw.js` and `index.html`).

**To add a PWA to production:** explicitly add it to the rsync allowlist in `deploy-prod.sh`. A new PWA in master does NOT go to production automatically. This is a deliberate deployment gate.

**Decision Maker:** hkl

## Alternatives Considered

- **Full git checkout into prod web root.** Rejected: exposes all PWAs in master (including in-progress) to production users; accidental `git commit` of an unfinished PWA to master would deploy it to prod on next deploy-prod.sh run.
- **Feature branches for in-progress PWAs (never merge to master until prod-ready).** Rejected: requires discipline across many concurrent PWA builds; in practice, partially-complete PWAs are committed to master for dev testing; the rsync allowlist is a simpler gate than branch discipline.
- **Docker image per PWA (build/push to registry, deploy container).** Rejected: significant operational overhead for what are static HTML files; the PWA architecture (ADR-013, single HTML files) does not benefit from containerization; rsync of a named file list is the appropriate tool.
- **Traefik routing rules to block prod access to non-MVP paths.** Rejected: routing rules would need to be updated every time a PWA is promoted — same effort as updating the rsync allowlist, but harder to audit (routing rules are not in version control as visibly as a deploy script).

## Consequences

**Positive:**
- In-progress PWAs in master are never accidentally deployed to production.
- Promoting a PWA to production is a single explicit line addition to `deploy-prod.sh` — one PR, one code review.
- deploy-prod.sh is in version control — the allowlist is auditable.

**Negative / Trade-offs:**
- Developer must remember to add their PWA to the rsync allowlist before expecting it to go live on prod — easy to forget after a feature is "complete."
- The allowlist in deploy-prod.sh must be manually maintained — no automation around "this PWA is ready for prod."

**Risks and mitigations:**
- PWA omitted from allowlist after dev completion: mitigated by deploy checklist (add to allowlist as part of "promote to prod" step); ADR-016 (PWA registry) requires prod=true flag in hub.pwa_registry — can serve as a secondary check.
- allowlist grows stale (removed PWA still listed): rsync of a path that doesn't exist is a no-op; stale entries cause no harm but should be cleaned up.

## Related Decisions

- ADR-015 (dev and prod two stacks on same VPS) — the two-stack setup that makes this allowlist approach necessary.
- ADR-016 (PWA registry in hub.pwa_registry) — the hub.pwa_registry prod=true flag is the user-visible complement to the rsync allowlist.
- ADR-017 (Traefik routing) — Traefik routes are stack-specific; prod Traefik only routes to prod web root paths.

## References

- `memory/dbt_infra.md` — deploy-prod.sh description, MVP PWA list
- `memory/infra_env_separation.md` — dev/prod two-stack architecture
- `deploy-prod.sh` — rsync allowlist (Admin, Hub, Custodian, Activity)
