# STA Cabin — FINAL Session End-of-Session Handoff

> **Purpose**: Definitive end-of-session document for the STA cabin work session of 2026-04-27. After this session, **STA cabin is feature-complete + prod-deploy-ready**. Designed as the single source of truth for any future session picking up STA work — operational, hygiene, or feature-extension.

**Date:** 2026-04-27 (session ran ~30h on 2026-04-26 → 2026-04-27)
**Branch (this doc):** `sta/session-eos-2026-04-27-final` (cut from `origin/main` `d29b7d84`)
**Scope of this doc:** comprehensive STA paridade work consolidated into one place. Supersedes prior per-Onda handoffs as the navigation entry point.

---

## Index

1. [Executive summary](#executive-summary)
2. [Complete PR catalog (15 PRs)](#pr-catalog)
3. [STA cabin status — feature complete](#status)
4. [Code surface inventory](#code-surface)
5. [Test coverage matrix](#tests)
6. [Configuration — env vars + AWS secrets](#config)
7. [Deploy-readiness — ALL green](#deploy-readiness)
8. [Operator runbook (initial deploy + daily ops)](#operator-runbook)
9. [Hard gates current state](#gates)
10. [Next session — 4 paths with starting points](#next-session)
11. [Lessons learned this session](#lessons)
12. [References](#references)

---

<a id="executive-summary"></a>
## 1. Executive summary

### What this session delivered

Started 2026-04-26 with STA cabin in a partial state — Manual STA Web Services v1.5 jul/2022 conformance was patchy, no resilient transfer support, no admin endpoints for queries, no rate-limit enforcement, no senha lifecycle, no admin RBAC. The cabin compiled but had **at least 6 known showstoppers** documented in prior handoffs.

By session end (~30h later, 12 STA backend/CI/infra PRs + 3 doc PRs = **15 PRs** total), the cabin is:

- ✅ Manual-conformant in all wire-format paths (Ondas 1+2.A+2.B+2.C+2.D)
- ✅ Resilient against transient failures (chunked uploads, ranged downloads, mid-stream resume, retry-cap)
- ✅ Rate-limited per Manual §2.6 (3-bucket façade)
- ✅ Lifecycle-aware (HTTP 410 per-op semantic split, 429 Retry-After, auto-renewal)
- ✅ Senha-managed (daily cron + 2-tier escalation)
- ✅ Admin-protected (G5 RBAC fully enforced)
- ✅ Deploy-plumbed (fargate-sta.tf + secrets + GitHub Actions workflow + internal Route53 routing)

**STA is the first Monetarie cabin to reach feature-complete + prod-deploy-ready status in this monorepo period.**

### What's NOT done (deferred — not blockers)

- **Onda 3.A** leiautes corpus + XSD validator (independent; ~1-2 days; ACCS001/ACCS010 prod traffic)
- **Onda 4** identifier mapping in `core/` (cross-cabin)
- 2.C-2 follow-up `protocol_expires_at` (only worth doing once admin UI consumer exists)
- Per-endpoint feature granularity refinement
- Operational tasks: `terraform apply`, secret value population, Aurora migration run, smoke test

---

<a id="pr-catalog"></a>
## 2. Complete PR catalog (15 PRs)

| # | PR | Merge SHA | Date | Type | Subject |
|---|---|---|---|---|---|
| 1 | #24 | `301503ec` | 2026-04-26 15:37 | fix | Onda 1 — P0 wire-format + ICP-Brasil chain |
| 2 | #30 | `3af17964` | 2026-04-26 18:08 | feat | Onda 2.A — chunked upload + ranged download + ETag |
| 3 | #34 | `ed03ce39` | 2026-04-26 19:58 | feat | Onda 2.A f1 — Outbound.Worker chunked-upload wiring |
| 4 | #36 | `100e5154` | 2026-04-26 20:57 | feat | Onda 2.A f3 — Operations.resume_upload/3 |
| 5 | #43 | `99e6e57c` | 2026-04-26 22:31 | feat | Onda 2.A f2 — ranged resume mid-download |
| 6 | #46 | `1c10b7fa` | 2026-04-26 23:43 | feat | Onda 2.A caller-wiring bundle |
| 7 | #52 | `fbf4dac2` | 2026-04-27 11:49 | fix | upload_attempts retry-cap (changeset bug) |
| 8 | #59 | `e54399ab` | 2026-04-27 12:05 | docs | 2.A complete + 2.B brainstorming pause handoff |
| 9 | #62 | `67924840` | 2026-04-27 13:25 | feat | Onda 2.B — status & queries (C full+admin) |
| 10 | #66 | `a55ad325` | 2026-04-27 13:59 | feat | Onda 2.C — rate & lifecycle (B trim) |
| 11 | #68 | `ac52cd89` | 2026-04-27 14:31 | docs | Comprehensive EOS handoff post-2.C |
| 12 | #70 | `01c7413d` | 2026-04-27 15:07 | feat | fargate-sta.tf + 3 secrets (deploy plumbing) |
| 13 | #71 | `9dfc7c76` | 2026-04-27 15:17 | fix | Reuse internal ALB target group (fix #70) |
| 14 | #74 | `78b01df7` | 2026-04-27 15:23 | ci | deploy-sta-homolog GitHub Actions workflow |
| 15 | #79 | `2de94ff0` | 2026-04-27 15:44 | feat | Onda 2.D — password mgmt (Full) |
| 16 | #83 | `3a5d21d1` | 2026-04-27 17:07 | feat | G5 admin API RBAC enforcement |

(15 PRs in the chronology + this final session-EOS handoff PR.)

---

<a id="status"></a>
## 3. STA cabin status — feature complete

### Manual conformance per Onda

| Manual § | Endpoint | Status | Onda |
|---|---|---|---|
| §5.1 | `POST /staws/arquivos` (request_protocol) | ✅ | 1 (B2+B3+B4 fixes) + 2.C (410 mapping) |
| §5.2 | `PUT /staws/arquivos/{p}/conteudo` (single upload) | ✅ | 1 (B1 fix) + 2.C (410 mapping) |
| §5.3 | `GET /staws/arquivos/{p}/posicaoupload` | ✅ | 2.A + 2.C (410 mapping) |
| §5.6 | `PUT /staws/arquivos/{p}/conteudo` (chunked w/Content-Range) | ✅ | 2.A core + 2.C (410 mapping) |
| §6.1 | `GET /staws/arquivos/{p}/conteudo` (full download) | ✅ | 2.A core (X-Content-Hash + ETag) + f2 (streaming variant) |
| §6.4 | `GET /staws/arquivos/{p}/conteudo` (Range) | ✅ | 2.A core (If-Match + 410/412/416/501) |
| §7.1 | `PUT /staws/arquivos/situacao` (change_status) | ✅ | 2.B (batched semicolon-joined Protocolos) |
| §8.1 | `GET /staws/arquivos/disponiveis` | ✅ | pré-existente |
| §8.2 | `GET /staws/arquivos?tipoConsulta=PROT` (query_protocol) | ✅ | 2.B (FileQueryResult) |
| §8.3 | `GET /staws/arquivos?tipoConsulta=AVANC` (advanced_query) | ✅ | 2.B (multi-param + a_rec warning) |
| §9.1 | `PUT https://www9/3.bcb.gov.br/senhaws/senha` | ✅ | 2.D (separate base URL) |
| §9.2 | `GET https://www9/3.bcb.gov.br/senhaws/senha/vencimento` | ✅ | 2.D (FileExpiry parser) |

**Not yet implemented (deferred):** Onda 3 leiautes corpus (44 leiautes / 9 cats), Onda 4 identifier mapping in `core/`. Both independent of paridade — they extend the cabin, don't fix gaps.

### Cross-cutting concerns

| Concern | Status | Source |
|---|---|---|
| HTTP 410 per-operation mapping (upload→`:protocol_expired` vs download→`:file_unavailable`) | ✅ | 2.C Task 2 |
| HTTP 429 Retry-After parsing (RFC 7231 §7.1.3) | ✅ | 2.C Task 3 |
| 3-bucket rate limiting (transfers/queries/status_changes per §2.6) | ✅ | 2.C Task 1 |
| Outbound.Worker auto-renewal on 410 (1 attempt cap) | ✅ | 2.C Task 4 |
| Outbound.Worker rate-limit honor (Process.send_after non-blocking) | ✅ | 2.C Task 4 |
| StateCode enum (Manual Tabela 3 — 14 codes + classifications) | ✅ | 2.B Task 1 |
| Worker.decide_next_action future-readiness (Onda 2.C state polling) | ✅ | 2.B Task 3 |
| Telemetry events (`[:sta, :rate_limiter, :acquire]`) | ✅ | 2.C |
| Senha daily cron (06:00 BRT) + 2-tier escalation (≤7d warning / ≤2d critical) | ✅ | 2.D |
| Oban full setup (deps + migration v13 + supervision + 3 queues) | ✅ | 2.D Task 3 |
| RBAC plug + super_admin bypass + role-fallback + ETS cache | ✅ | G5 |

### Test surface

301 tests passing across 15 test files (6 pre-existing `Soap.parse_response/2` baseline failures preserved — pre-Onda-1 SOAP path noise; production uses REST puro). 5 baseline compile warnings preserved exactly. ~2.6s combined batch run.

---

<a id="code-surface"></a>
## 4. Code surface inventory

### Modules created this session

```
sta/backend/lib/sta_connector/
├── rbac.ex                                          ★ G5
├── rbac/
│   ├── module.ex (existing schema)
│   ├── feature.ex (existing schema)
│   ├── group.ex (existing schema)
│   ├── group_feature.ex (existing schema)
│   └── user_group.ex (existing schema)
├── sta/
│   ├── client.ex                                    (extended: 12 operations now)
│   ├── client_behaviour.ex                          (extended: 12 callbacks)
│   ├── operations.ex                                (extended: resilient transfer + resume)
│   ├── error.ex                                     (existing)
│   ├── soap.ex                                      (legacy — REST puro is canonical now)
│   ├── types.ex                                     (★ FileQueryResult NEW + others)
│   ├── state_code.ex                                ★ 2.B (Tabela 3 enum)
│   ├── password_manager.ex                          ★ 2.D (GenServer with notify decision)
│   ├── rate_limiter.ex                              (extended: 3 buckets + telemetry + retro-compat)
│   └── rate_limiter/
│       ├── transfers.ex                             ★ 2.C
│       ├── queries.ex                               ★ 2.C
│       └── status_changes.ex                        ★ 2.C
├── inbound/
│   └── worker.ex                                    (extended: decide_next_action future-readiness)
├── outbound/
│   └── worker.ex                                    (extended: decide_next_action + dispatch_upload_error + protocol_renewed flag)
├── outbound.ex                                      (extended: clear_protocol/1)
└── workers/
    └── password_expiry_worker.ex                    ★ 2.D (Oban.Worker)

sta/backend/lib/sta_connector_web/
├── controllers/api/admin/
│   ├── outbound_files_controller.ex                 ★ 2.A f6 (resume_upload)
│   ├── sta_query_controller.ex                      ★ 2.B (protocols + advanced)
│   └── ... (8 other admin controllers — all wired with G5 plug)
└── plugs/
    └── require_permission.ex                        ★ G5

sta/backend/priv/
├── certs/
│   └── icp_brasil_ca_chain.pem                      ★ Onda 1 (mTLS fail-CLOSED)
└── repo/migrations/
    ├── 20260426150000_add_resilient_transfer_columns.exs   ★ 2.A
    └── 20260427153455_create_oban_jobs.exs                 ★ 2.D

infra/aws/greenfield/
├── fargate-sta.tf                                   ★ deploy plumbing (PR #70+#71)
└── secrets.tf                                       (extended: 3 STA secret shells)

.github/workflows/
└── deploy-sta-homolog.yml                           ★ deploy plumbing (PR #74)
```

### New `outbound_files` schema columns (PR #30 migration)

| Column | Type | Default | Purpose |
|---|---|---|---|
| `posicao_upload` | `:map` | `%{}` | RangesRecebidos snapshot (Manual §5.3.1 chunked reconciliation) |
| `bytes_uploaded` | `:bigint` | `0` | Idempotent sum of ACKed chunk bytes |
| `etag` | `:string` | nil | RFC 7232 If-Match preconditions |
| `last_modified` | `:string` | nil | RFC 7232 §6.1.4 |
| `x_content_hash` | `:string` | nil | SHA-256 hex (Manual §6.1) |
| `upload_attempts` | `:integer` | `0` | Retry counter (PR #52 fix) |

**All forward-only safe** (nullable / default-zero, no rename, no drop, no constraint).

### Public HTTP endpoints (`/api/admin/*`, `:admin_api` pipeline with G5 RBAC)

| Method | Path | Plug feature/action | Source |
|---|---|---|---|
| POST | `/api/admin/outbound_files/:id/resume_upload` | outbound.pendentes/:edit | 2.A f6 |
| GET | `/api/admin/sta/protocols?ids=...&detail=...&origin=...` | protocols.historico/:view | 2.B |
| GET | `/api/admin/sta/queries/advanced?date_creation_from=...&...` | protocols.historico/:view | 2.B |
| (8 other existing admin endpoints across 9 controllers) | (per G5 wiring matrix) | G5 |

### Telemetry events emitted

| Event | Source | Wave 11 P2 home |
|---|---|---|
| `[:sta, :rate_limiter, :acquire]` | RateLimiter (2.C) | TODO Prometheus collector |

---

<a id="tests"></a>
## 5. Test coverage matrix

### Touchstone files (15 total)

```bash
mix test \
  test/sta_connector/sta/client_test.exs \                                                     # 101 / 6 baseline
  test/sta_connector/sta/operations_test.exs \                                                 # 58 / 0
  test/sta_connector/files/outbound_file_test.exs \                                            # 7 / 0
  test/sta_connector/application_test.exs \                                                    # 3 / 0
  test/sta_connector/outbound_test.exs \                                                       # 38 / 0
  test/sta_connector_web/controllers/api/admin/outbound_files_controller_test.exs \            # 7 / 0
  test/sta_connector/sta/state_code_test.exs \                                                 # 14 / 0
  test/sta_connector/inbound/worker_test.exs \                                                 # 3 / 0
  test/sta_connector/outbound/worker_test.exs \                                                # 8 / 0
  test/sta_connector_web/controllers/api/admin/sta_query_controller_test.exs \                 # 16 / 0
  test/sta_connector/sta/rate_limiter_three_bucket_test.exs \                                  # 14 / 0
  test/sta_connector/sta/password_manager_test.exs \                                           # 10 / 0
  test/sta_connector/workers/password_expiry_worker_test.exs \                                 # 5 / 0
  test/sta_connector/rbac_test.exs \                                                           # 9 / 0
  test/sta_connector_web/plugs/require_permission_test.exs                                    # 8 / 0

# Result: 301 tests, 6 failures (Soap baseline preserved)
# Time:   ~2.6s (2.3s async + 0.3s sync)
```

### Test surface evolution across Ondas

| Snapshot | Tests | Failures | Δ from prev |
|---|---|---|---|
| Pre-Onda-1 | ~150 | varied | baseline |
| Post-Onda-1 (PR #24) | ~150 | 6 (Soap baseline) | wire-format pinned |
| Post-Onda-2.A (6 PRs) | ~199 | 6 | +chunked+ranged tests |
| Post-Onda-2.B (PR #62) | ~235 | 6 | +query_protocol + advanced + state_code |
| Post-Onda-2.C (PR #66) | ~262 | 6 | +rate_limiter + 410 + 429 + dispatch |
| Post-Onda-2.D (PR #79) | ~284 | 6 | +password mgmt + Oban worker |
| Post-G5 (PR #83) | **301** | 6 | +Rbac + RequirePermission |

**Compile**: 5 baseline warnings preserved across every commit. No new warnings introduced this session.

### Pre-existing baseline failures (NOT investigate)

- 6 in `client_test.exs` `Soap.parse_response/2` describe (legacy SOAP path — production uses REST puro)
- ~21 in `inbound/poller_test.exs`, `supervisor_test.exs`, `router_test.exs`, `poller_integration_test.exs` (DB-setup gaps; hygiene PR isolado)

`mix test` full-suite is broken — always use the 15-file batch above.

---

<a id="config"></a>
## 6. Configuration — env vars + AWS secrets

### Production env vars (read by `runtime.exs` + `Application.get_env`)

| Var | Default | Source | Notes |
|---|---|---|---|
| **Database** | | | |
| `DATABASE_URL` | (raise) | Aurora secret | Aurora `monetarie-sta-homolog` (auto-built in `aurora.tf`) |
| `DATABASE_SSL` | `"true"` | env | TLS to Aurora |
| `POOL_SIZE` | `"10"` | env | Ecto pool |
| **Phoenix** | | | |
| `SECRET_KEY_BASE` | (raise) | secret | `monetarie/{env}/auth/mon_sta/secret_key_base` |
| `PHX_HOST` | `staapi.internal.monetarie.com.br` | task def | Internal-only via Route53 private (NOT ProDNS) |
| `PHX_SERVER` | `"true"` | task def | Bandit start |
| `PORT` | `"4000"` | task def | HTTP port |
| **STA** | | | |
| `STA_ENVIRONMENT` | `"homologation"` | task def | Toggles BCB endpoint host |
| `STA_TIMEOUT_MS` | `"30000"` | task def | Finch timeout |
| `STA_MAX_RETRIES` | `"3"` | task def | Operations retry budget |
| `STA_BCB_USERNAME` | (raise) | **secret** ★ | `monetarie/{env}/sta/bcb_username` |
| `STA_BCB_PASSWORD` | (raise) | **secret** ★ | `monetarie/{env}/sta/bcb_password` |
| `STA_ADMIN_PASSWORD` | (raise in prod) | **secret** ★ | `monetarie/{env}/admin/sta/initial_password` |
| `STA_WORKERS_ENABLED` | `"false"` first deploy → `"true"` post-smoke | task def | Conservative posture in `fargate-sta.tf` |
| `STA_PWD_EXPIRY_WARN_DAYS` | `"7"` | task def | Threshold for `:warning` notification |
| `STA_PWD_EXPIRY_ERROR_DAYS` | `"2"` | task def | Threshold for `:critical` notification |
| **Poller** | | | |
| `POLLER_ENABLED` | `"false"` first deploy → `"true"` post-smoke | task def | Conservative posture |
| `POLLER_INTERVAL_MS` | `"60000"` | task def | 1min ticks |
| **Rate limit (legacy keys, still honored)** | | | |
| `RATE_LIMIT_MAX_CONCURRENT` | `"10"` | task def | `:transfers` bucket |
| `RATE_LIMIT_MAX_QPM` | `"120"` | task def | `:queries` bucket |
| **mTLS** | | | |
| `ICP_BRASIL_CHAIN_PATH` | `priv/certs/icp_brasil_ca_chain.pem` | container | Override for Secrets Manager source |
| **NATS** | | | |
| `NATS_HOSTS` | unset (uses fallback) | task def | Comma-sep `host:port` list |
| **Misc** | | | |
| `SIMULATOR_ENABLED` | `"false"` | task def | NEVER true in BCB-real environments |

### AWS Secrets Manager state (post `terraform apply`)

| Secret name | Status | Value source |
|---|---|---|
| `monetarie/{env}/db/mon_sta/password` | ✅ Provisioned + populated | `random_password.db_password["mon_sta"]` (auto) |
| `monetarie/{env}/db/mon_sta/url` | ✅ Provisioned + populated | Auto-built from cluster endpoint + password (`aurora.tf`) |
| `monetarie/{env}/auth/mon_sta/secret_key_base` | ✅ Provisioned + populated | `random_password.secret_key_base["mon_sta"]` |
| `monetarie/{env}/auth/mon_sta/guardian_secret` | ✅ Provisioned + populated | `random_password.guardian_secret["mon_sta"]` |
| `monetarie/{env}/bacen/ispb` | ✅ Provisioned + populated | "46026562" |
| `monetarie/{env}/sta/bcb_username` | ✅ Shell created (**values pending operator**) | Manual `aws secretsmanager put-secret-value` |
| `monetarie/{env}/sta/bcb_password` | ✅ Shell created (**values pending operator**) | Manual put-secret-value |
| `monetarie/{env}/admin/sta/initial_password` | ✅ Shell created (**values pending operator**) | Manual put-secret-value |

### IAM (zero changes needed in this session)

ECS execution role policy in `ecs-cluster.tf` covers `monetarie/{env}/*` wildcard + all `aws_kms_key.service` entries via `for k in` collection. The 3 new STA-specific secrets fit the existing wildcard.

---

<a id="deploy-readiness"></a>
## 7. Deploy-readiness — ALL green

| Item | Status | Source |
|---|---|---|
| Backend Elixir compiles | ✅ | mix compile (5 baseline warnings) |
| Tests pass | ✅ | 301/295 deterministic (6 baseline failures) |
| Migrations forward-safe | ✅ | 8 migrations including Oban v13 (PR #79) |
| Dockerfile multi-stage | ✅ | `sta/backend/Dockerfile` |
| mTLS configured | ✅ | `priv/certs/icp_brasil_ca_chain.pem` (PR #24) |
| Aurora `monetarie-sta-homolog` provisioned | ✅ | `aurora.tf` |
| KMS + base secrets | ✅ | `secrets.tf` |
| `fargate-sta.tf` (ECS task def + service) | ✅ | PR #70+#71 |
| Internal Route53 routing (`staapi.internal.monetarie.com.br`) | ✅ | PR #71 + `admin-internal-routing.tf` |
| 3 STA secret shells | ✅ | PR #70 |
| GitHub Actions OIDC workflow | ✅ | PR #74 |
| `StaConnector.Release.migrate/0` | ✅ | Pre-existing |
| Admin RBAC enforcement | ✅ | PR #83 (G5) |
| Senha lifecycle (cron + alerts) | ✅ | PR #79 (Onda 2.D) |

**100% code/CI/TF deploy-ready.** Pending items are 100% operational.

### What operator must do (initial deploy)

```bash
# 1. Apply infrastructure (idempotent)
cd infra/aws/greenfield
terraform plan
terraform apply

# 2. Populate 3 STA-specific secret values
aws secretsmanager put-secret-value \
  --secret-id monetarie/homolog/sta/bcb_username \
  --secret-string '<sisbacen-operator-user>' \
  --profile monetarie --region sa-east-1

aws secretsmanager put-secret-value \
  --secret-id monetarie/homolog/sta/bcb_password \
  --secret-string '<sisbacen-operator-pass>' \
  --profile monetarie --region sa-east-1

aws secretsmanager put-secret-value \
  --secret-id monetarie/homolog/admin/sta/initial_password \
  --secret-string '<strong-pass-rotate-on-first-login>' \
  --profile monetarie --region sa-east-1

# 3. Trigger first build via GitHub Actions
git push  # any sta/backend/** change OR manually trigger workflow_dispatch

# 4. Run migrations one-shot
aws ecs run-task \
  --cluster monetarie-greenfield-homolog \
  --task-definition monetarie-sta-api-homolog \
  --launch-type FARGATE \
  --network-configuration ... \
  --overrides '{"containerOverrides":[{"name":"sta-api","command":["bin/sta_connector","eval","StaConnector.Release.migrate()"]}]}' \
  --profile monetarie --region sa-east-1

# 5. Bump desired_count from 0 to 1
aws ecs update-service \
  --cluster monetarie-greenfield-homolog \
  --service sta-api \
  --desired-count 1 \
  --profile monetarie --region sa-east-1

# 6. Verify via VPN
curl -fsS https://staapi.internal.monetarie.com.br/health
curl -fsS https://staapi.internal.monetarie.com.br/ready

# 7. Run RBAC seeds
aws ecs run-task ... --overrides '...command:["bin/sta_connector","eval","Code.eval_file(\"priv/repo/seeds/rbac_seed.exs\")"]'

# 8. Smoke test admin login + a couple admin endpoints
TOKEN=$(curl -fsS -X POST https://staapi.internal.monetarie.com.br/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin@monetarie.com.br","password":"<initial>"}' \
  | jq -r .token)
curl -fsS -H "Authorization: Bearer $TOKEN" https://staapi.internal.monetarie.com.br/api/admin/health

# 9. Once smoke green: enable workers
# Update task def env vars: STA_WORKERS_ENABLED=true POLLER_ENABLED=true
# Then `aws ecs update-service --force-new-deployment`
```

### Rollback (if needed)

- **Health failures**: `aws ecs update-service --task-definition <PREV_REVISION>` rolls back to previous task def. Or `--desired-count 0` to halt.
- **Migration failures**: `Release.rollback("StaConnector.Repo", "<previous_version>")` via run-task. Aurora has automatic snapshots if PITR needed.
- **BCB 401 storm**: update Secrets Manager value + `--force-new-deployment`. Worker retry-cap (PR #52) protects against runaway retries.

---

<a id="operator-runbook"></a>
## 8. Operator runbook

### Daily ops (zero intervention expected)

- Cron at 06:00 BRT (= 09:00 UTC) fires `PasswordExpiryWorker`.
- If `days_remaining > 7`: silent (logged at info level only).
- If `days_remaining ≤ 7`: `Notifications.notify(:warning, ...)` — surfaces via PubSub to admin frontend + persisted in Notifications context.
- If `days_remaining ≤ 2`: `Notifications.notify(:critical, ...)`.

### Alert response — senha expiring

```bash
# 1. Trigger BCB password change via console
aws ecs run-task ... --command [..., "eval",
  "{:ok, :ok} = StaConnector.Sta.Client.password_change(%{
    current_password: System.get_env(\"STA_BCB_PASSWORD\"),
    new_password: \"<NEW>\"
  })"]

# 2. Update Secrets Manager
aws secretsmanager update-secret \
  --secret-id monetarie/homolog/sta/bcb_password \
  --secret-string '<NEW>'

# 3. Force new deployment (re-reads secret)
aws ecs update-service \
  --cluster monetarie-greenfield-homolog \
  --service sta-api \
  --force-new-deployment

# 4. Verify
aws ecs run-task ... --command [..., "eval",
  "StaConnector.Sta.PasswordManager.check_expiry()"]
# Expected: {:ok, %{days_remaining: ~90, notified: :none}}
```

### Add new operator/auditor user

```elixir
# bin/sta_connector eval "..."
{:ok, user} = StaConnector.Auth.create_user(%{
  email: "operator@monetarie.com.br",
  name: "Operator",
  password: "...",
  role: "operator"  # → role-fallback grants :view/:create/:edit/:export
})

# Optionally link to an explicit group
{:ok, _ug} = StaConnector.Repo.insert(%StaConnector.Rbac.UserGroup{
  user_id: user.id,
  group_id: <group_id from Rbac.list_groups>
})

# Cache invalidation (post group_features change)
StaConnector.Rbac.invalidate_cache()
```

### Worker auto-renewal on protocol expiry

- BCB returns HTTP 410 on a protocol older than 48h (Manual §5).
- `Outbound.Worker.dispatch_upload_error/2` → `{:renew_protocol, ...}` → `Outbound.clear_protocol/1` → restart `:start_upload`.
- Cap = 1 renewal per worker lifetime to avoid loop. State.protocol_renewed flag.
- After cap: `mark_failed/2`.

### Rate-limit honor (HTTP 429)

- BCB returns 429 with optional `Retry-After: <seconds>` header.
- Client returns `{:error, {:rate_limited, n}}`.
- `Outbound.Worker.dispatch_upload_error/2` → `{:retry_after, n*1000}` → `Process.send_after(self(), {:retry_upload, ...}, n*1000)`.
- Non-blocking; worker GenServer waits on the message.
- Retry-cap honored.

### CloudWatch log queries (operational)

```sql
-- 403 RBAC denials
fields @timestamp, @message
| filter @message like /\[RequirePermission\] 403/
| sort @timestamp desc

-- 410 protocol_expired auto-renewals
fields @timestamp, @message
| filter @message like /STA returned HTTP 410.*auto-renewing/
| sort @timestamp desc

-- 429 rate-limited retries
fields @timestamp, @message
| filter @message like /rate-limited.*Retry-After/
| sort @timestamp desc
```

---

<a id="gates"></a>
## 9. Hard gates current state

| Gate | Description | Status | Blocks STA homol? | Blocks STA prod? |
|---|---|---|---|---|
| **G1** | Certisign T001 cert (SPB BACEN auth) | ⏳ Aguardando ~1 sem | NÃO | NÃO |
| **G2** | Aurora drop+recreate (user authorization) | ⏳ User authorization | SIM (migrations) | SIM |
| **G3** | BCB MQ creds (SPB only) | ⏳ | NÃO | NÃO |
| **G4** | STA upload + GEN0006 ACCP (SPB cert activation) | ⏳ Depends G1 + STA homolog working | NÃO | NÃO |
| **G5** | STA admin auth (`:admin_api` RBAC) | ✅ **CLOSED PR #83** | — | — |
| **G6** | STA_BCB_USERNAME/PASSWORD provisionados | ⏳ User input | SIM | SIM |

**STA-specific blockers**: G2 (Aurora migration run) + G6 (BCB credentials populated). Both 100% operational tasks; zero code work needed.

**G5 closed this session** — STA was the FIRST cabin to fully close G5. PIX/SPB/NPC/CLST will close G5 separately.

---

<a id="next-session"></a>
## 10. Next session — 4 paths with starting points

### Path A — Onda 3.A leiautes corpus + XSD validator ★ recommended

**Branch**: cut `sta/onda3a-leiautes-corpus-validator` off `origin/main`.

**Plan source**: `sta/docs/plans/2026-04-26-sta-conformance-rollout.md` §Sub-PR 3.A (lines 809-836).

**Estimated effort**: 1-2 days / ~400 LOC code + 2-3 MB corpus.

**Deliverables**:
- `sta/backend/priv/leiautes-rsfn/{ccs,cir,cmp,gen,lfl,ptx,scg,sel,str}/` directory tree (44 files from BCB RSFN page)
- `sta/scripts/sync_leiautes_rsfn.sh` re-sync script (wget/curl with checksums)
- `sta/backend/lib/sta_connector/parsers/xsd_validator.ex` wrapper over `:xmerl_xsd` with ETS cache
- `sta/backend/lib/sta_connector/crypto/xml_signer.ex` Behaviour interface (real impl swapped in via runtime config when Compliance confirms cert source)
- `sta/backend/lib/sta_connector/crypto/xml_signer/stub.ex` default impl returning `{:error, :not_configured}`
- Tests for both

**Acceptance**:
- `priv/leiautes-rsfn/` contains all 44 files (verifiable via `scripts/sync_leiautes_rsfn.sh --check`)
- XsdValidator validates 5 known-good and 5 known-bad samples correctly
- XmlSigner interface stable (later replacement only swaps the impl module)

**Why recommended**: independent of all prior work, destrava ACCS001/ACCS010 prod traffic for Compliance. Logical next step in the rollout plan.

**Pre-flight before starting**: confirm with Compliance whether XSDs are public (likely; BCB RSFN page is operator-accessible). If gated by Compliance approval for cert source, defer to Path D and pick a hygiene PR instead.

### Path B — Onda 4 identifier mapping in `core/`

**Branch**: cut `sta/onda4-identifier-mapping` off `origin/main`.

**Plan source**: `sta/docs/plans/2026-04-26-sta-conformance-rollout.md` §Sub-PR 4.A (lines NOT in extract; consult full doc).

**Estimated effort**: ~1 day / ~200 LOC.

**Cross-cabin warning**: touches `core/`. **MUST coordinate with Core team** before starting. Original session prompt forbade `core/*` modifications; check current policy.

**Deliverables**:
- `Monetarie.Regulatory.StaDelivery` extensions for CCS→PCCS001, etc. mapping
- Identifier translation layer between internal Monetarie systems and STA system_id codes

### Path C — Hygiene PRs (multiple small ones)

**Candidates**:
- Full `worker_test.exs` for Inbound.Worker / Outbound.Worker (currently only `decide_next_action/1` covered)
- 2.C-2 follow-up `protocol_expires_at` field (only worth doing once admin UI consumer exists)
- Per-endpoint feature granularity refinement (current G5 mapping is coarse 1-2 features per controller)
- Soap.parse_response baseline failures investigation + cleanup
- Inbound poller_test/router_test/supervisor_test DB-setup gap fixes (~21 baseline failures)
- ARM64/Graviton migration (current task def is X86_64; CLAUDE.md aspires to ARM64)

**Each is 0.5-1 day.** Pick one based on operator pain.

### Path D — Stop, encerrar STA cabin work

**Justified if**: STA is in good shape; team needs to focus on PIX/SPB/NPC/CLST or new features in core.

STA cabin won't bit-rot — wire-format is pinned to Manual v1.5 jul/2022 (won't change unless BCB updates spec); RBAC is enforced; senha lifecycle is automated; deploy is fully plumbed.

---

### Recommended decision tree

1. **Operator deploys STA today?** → defer ALL paths until smoke validates production. Then resume per priority.
2. **Compliance has cert source confirmed?** → Path A.
3. **Compliance NOT ready?** → Path C (hygiene) or Path D (stop).
4. **Other cabin urgent?** → Path D + switch to that cabin.

---

<a id="lessons"></a>
## 11. Lessons learned this session

### Pattern: Manual Verification gate WORKS

Every Onda 2.x sub-PR included a Task 0 Manual Verification inventory in its plan doc. This caught:
- 4 misdiagnoses in Onda 2.A core (chunked upload wire format)
- HTTP 410 semantic split between §5.6 (upload) and §6.1/§6.4 (download) in Onda 2.C — central insight
- Senha base URL separation (www9/www3) in Onda 2.D
- Zero misdiagnoses in 2.B and G5 (those were thoroughly Manual-aligned)

**Takeaway**: any sub-PR with new BCB-facing wire format claims MUST start with Task 0 Manual citation. Internal/architectural PRs (G5) can skip.

### Pattern: TDD red→green→commit per task

Every task across Ondas 2.A-2.D + G5 followed this discipline. Result: 162 new tests added in this session, 0 regressions in any touchstone, 5 baseline warnings preserved EXACTLY.

**Takeaway**: invariant. Don't break it.

### Pattern: trim vs full scope decisions per sub-PR

For each Onda 2.x I presented 3 scope options to user and let them pick. Ratios:
- 2.B → user picked full+admin (C)
- 2.C → user picked trim (B)
- 2.D → user picked full (A)
- G5 → user picked big-bang full (A)

**Takeaway**: explicit scope discussion at sub-PR start prevents mid-flight scope creep AND post-merge regret.

### Anti-pattern: orphan API without caller

PR #36 (Operations.resume_upload/3) shipped without a caller for ~1 day. PR #46 wired it. Wasted ~1h of context-switching.

**Takeaway** (applied in 2.B and G5): ship API + caller in same PR (option C in 2.B was explicitly this), OR explicitly defer the API until a caller is needed.

### Anti-pattern: pre-flight skip = wasted PR

PR #70 (`fargate-sta.tf`) used the public ALB pattern (`fargate-core-api.tf`) without checking `admin-internal-routing.tf` which already had `staapi` registered for the internal ALB. PR #71 was a pure-fix-up commit.

**Takeaway**: before copying a TF template, `grep -l "<service>" infra/aws/greenfield/*.tf` to see what's already wired.

### Bug fix surfaced this session

**`require_permission_test.exs` setup deleted bypass flag globally** — 29 touchstone failures in batch run. Fix: `put_env(false)` + `on_exit` restore to `true`. Documented in commit `5fc78f91`.

**Takeaway**: when test setup mutates application env, ALWAYS restore via on_exit (never leave it deleted). Async-false batch runs share env state.

### Tooling lesson: terraform validate sem creds

`terraform init -backend=false` permits `terraform validate` without S3 backend / AWS creds. Useful in CI / preview workflows.

### Documentation patterns that worked

- Per-Onda plan doc with Task 0 inventory + Architecture + Pre-flight + Tasks + Acceptance + Out of scope + References — used in all 5 Ondas.
- Per-Onda end-of-session handoff with cross-references to all prior handoffs.
- Comprehensive deploy-readiness handoff (PR #68) before deploy plumbing started — explicitly called out the gaps before solving them.

---

<a id="references"></a>
## 12. References

### Plan docs (in chronological order of merge)

| Plan | Path | Onda |
|---|---|---|
| Conformance rollout master | `sta/docs/plans/2026-04-26-sta-conformance-rollout.md` | All Ondas |
| Onda 2.A core | `sta/docs/plans/2026-04-26-sta-onda2a-resilient-transfer.md` | 2.A |
| Onda 2.A f2 ranged-resume | `sta/docs/plans/2026-04-26-sta-onda2a-followup-2-ranged-resume.md` | 2.A f2 |
| Onda 2.A caller-wiring | `sta/docs/plans/2026-04-26-sta-onda2a-followup-caller-wiring.md` | 2.A f6 |
| Onda 2.B status & queries | `sta/docs/plans/2026-04-27-sta-onda2b-status-and-queries-full-admin.md` | 2.B |
| Onda 2.C rate & lifecycle | `sta/docs/plans/2026-04-27-sta-onda2c-rate-and-lifecycle-trim.md` | 2.C |
| Onda 2.D password mgmt | `sta/docs/plans/2026-04-27-sta-onda2d-password-mgmt-full.md` | 2.D |
| G5 admin RBAC | `sta/docs/plans/2026-04-27-sta-g5-admin-rbac.md` | G5 |

### Handoff docs (in order of relevance for resumption)

| Handoff | Path | Notes |
|---|---|---|
| **THIS DOC** | `sta/docs/handoff/2026-04-27-sta-FINAL-SESSION-EOS.md` | **Primary entry point** for any future session |
| Comprehensive (pre-2.D + G5) | `sta/docs/handoff/2026-04-27-sta-paridade-EOS-COMPREHENSIVE.md` | Was definitive at PR #68 time; superseded by THIS DOC |
| Onda 2.B EOS | `sta/docs/handoff/2026-04-27-sta-onda2b-full-admin-complete.md` | Sub-Onda specific |
| Onda 2.C EOS | `sta/docs/handoff/2026-04-27-sta-onda2c-trim-complete.md` | Sub-Onda specific |
| Onda 2.D EOS | `sta/docs/handoff/2026-04-27-sta-onda2d-full-complete.md` | Sub-Onda specific |
| G5 EOS | `sta/docs/handoff/2026-04-27-sta-g5-admin-rbac-complete.md` | Sub-PR specific |
| Brainstorming pause (2.A→2.B transition) | `sta/docs/handoff/2026-04-27-sta-onda2b-brainstorming-pause-and-onda2a-complete.md` | Historical |

### Manual STA references

- `sta/Manual_STA_Web_Services.pdf` — v1.5 jul/2022 (canonical PDF).
- `/tmp/manual_sta.txt` — extract via `pdftotext -layout` (regenerable).
- §2.6 rate caps (10 simultâneos + 120/min)
- §5 envio (5.1 protocol, 5.2 single, 5.3 posicao, 5.6 chunked)
- §6 recebimento (6.1 full, 6.2 resume endorsement, 6.4 ranged)
- §7 alteração de situação
- §8 consultas (8.1, 8.2, 8.3 + Tabelas 1-3)
- §9 senha (9.1 alteração, 9.2 vencimento)

### RFCs

- RFC 6585 §4 — HTTP 429
- RFC 7231 §7.1.3 — Retry-After
- RFC 7232 — ETag, If-Match, Last-Modified
- RFC 7233 — Range requests
- RFC 5754 — SHA-2

### Infra docs

- `infra/aws/greenfield/CLAUDE.md` — TF root
- `docs/plans/2026-04-23-aws-greenfield-design.md` — design completo
- `infra/aws/greenfield/fargate-sta.tf` — STA Fargate (PR #70+#71)
- `infra/aws/greenfield/admin-internal-routing.tf` — Route53 private + internal ALB target groups
- `.github/workflows/deploy-sta-homolog.yml` — CI/CD (PR #74)

---

## TL;DR for very-future-self

```
STA cabin: feature-complete + prod-deploy-ready as of 2026-04-27 17:07Z (PR #83 merge).
12 backend/CI/infra PRs + 3 doc PRs = 15 total. 301 tests. 5 baseline warnings.

Code/CI/TF: 100% green.
Operational pending: terraform apply + 3 secret values + Aurora migration + smoke.
Hard gates: G2 + G6 (operational only). G5 closed this session.

Next session entry points (pick one):
  A) Onda 3.A leiautes corpus       ★ recommended
  B) Onda 4 identifier mapping       (cross-cabin)
  C) Hygiene PRs                     (multiple small)
  D) Stop, switch cabin              (STA stable)

Read this handoff first. Then per-Onda EOS docs if drilling into history.
```

---

**End of session 2026-04-27.** STA cabin is ready for whatever comes next.
