Skip to content

STORY-F-005: Bitwarden finnest project + GitHub Actions ci-finnest.yml

Epic: Infrastructure Priority: Must Have Story Points: 2 Status: Not Started Assigned To: Unassigned Created: 2026-04-17 Sprint: 1


User Story

As the Lead Developer, I want Finnest secrets living in Bitwarden Secrets Manager (carry-forward from AgenticAI-app) and CI running on every push via GitHub Actions on the existing bastion runners, so that secrets never live in git (SE-07, SE-12) and every commit is compile-checked, format-checked, Credo-checked, Dialyzer-checked, boundary-checked, and test-suite-checked before merge.


Description

Background

ADR-014-F §Reuse inventory commits to Bitwarden Secrets Manager (same org, new finnest project) and GitHub Actions runners on the existing CI bastion (13.236.155.22). No new infrastructure needed — reuse AgenticAI-app's proven pattern.

This story bootstraps both. No deployment yet (F-011 adds Kamal to integration); this is compile + test + lint in CI.

Scope

In scope:

  • Bitwarden SM admin action: create finnest project in the AgenticAI organisation's Bitwarden; create machine account; generate access token
  • Seed initial secrets (values set by ops):
  • FINNEST_DB_PASSWORD (local dev)
  • FINNEST_SECRET_KEY_BASE (Phoenix session signing)
  • FINNEST_CLOAK_KEY_V1 (application-level encryption, generated via mix phx.gen.secret)
  • ANTHROPIC_API_KEY (for F-013)
  • SMSGLOBAL_API_KEY, SMSGLOBAL_API_SECRET (reuse AgenticAI-app credentials initially)
  • scripts/deploy-finnest.sh — mirror of AgenticAI-app scripts/deploy.sh — uses bws CLI to fetch secrets before invoking Kamal (Kamal itself wired in F-011)
  • .github/workflows/ci-finnest.yml — runs on every push to any branch + PR to main:
  • Checkout
  • Setup Elixir + Erlang via erlef/setup-beam@v1 matching .tool-versions
  • Cache _build + deps
  • mix deps.get --only prod for prod check, mix deps.get for dev/test
  • mix format --check-formatted
  • MIX_ENV=prod mix compile --warnings-as-errors
  • mix credo --strict
  • mix dialyzer (with cached PLT)
  • mix test --cover
  • mix sobelow --config (SE-11)
  • mix deps.audit (SE-11)
  • mix boundary (AR-08, added after F-002 exists)
  • gitleaks step (SE-12)
  • Tag runner label to pin to CI bastion runners (runs-on: [self-hosted, linux, ci-bastion])
  • Document bws setup in CLAUDE.md addendum (developer onboarding)

Out of scope:

  • Kamal deploy config (F-011)
  • Production deployment workflow (F-020)
  • IRAP-specific pipeline (Phase 3, not this PRD scope)
  • Dependency soak / Renovate configuration (F-020 or Phase 1)

Technical Notes

  • Bitwarden SM access: Gautham has Org admin; create finnest project via Bitwarden web UI or bws project create
  • Machine account token format: BWS_ACCESS_TOKEN=0.xxx.yyy. Store in GitHub Actions secret BWS_FINNEST_TOKEN
  • CI runners on bastion already have bws binary installed (carry-forward from AgenticAI-app); verify with which bws
  • Dialyzer PLT cache key: ${{ runner.os }}-plt-${{ hashFiles('**/mix.lock') }}
  • mix test --cover reports coverage to stdout; fail if below 80% (CQ-02 — but first few sprints may have lower coverage, so don't fail on coverage in Sprint 1; add threshold gate in F-020 or Phase 1)
  • Secrets are NOT injected into CI test runs — tests use Mock adapters (F-013) / factories / deterministic seeds. CI secret access is only for deploy workflows (F-011/F-020).

Dependencies

  • Blocked by: STORY-F-001 (umbrella + mix.exs must exist for deps to resolve), STORY-F-002 (boundary dep)
  • Blocks: STORY-F-011 (deploy scripts assume this pattern)

Acceptance Criteria

  • Bitwarden finnest project visible in Bitwarden SM web UI
  • Machine account token created; stored as GitHub Actions secret BWS_FINNEST_TOKEN on the MMGS-Softnet-Pty-Ltd/finnest-planning repo (migrate to finnest code repo when that exists)
  • scripts/deploy-finnest.sh script exists; scripts/deploy-finnest.sh --help prints usage; --dry-run flag works without invoking Kamal
  • .github/workflows/ci-finnest.yml exists; triggers on push + PR
  • First CI run succeeds: compile, format, credo, dialyzer, test all green
  • CI run time: <5 min target on clean clone; <2 min on warm cache
  • Deliberate test: push a mix format violation → CI fails (demonstrate, then revert)
  • Deliberate test: push a Credo strict violation → CI fails (demonstrate, then revert)
  • gitleaks step catches a deliberate fake-secret commit in a feature branch (demonstrate, then revert)
  • Documentation: brief section in repo CLAUDE.md on "Running CI locally" + "Adding a new secret"

Testing Requirements

  • CI workflow tested by opening a PR with a trivial change; all steps pass
  • Deliberate-failure tests above prove each gate actually blocks on violation
  • Secret-rotation dry-run: change FINNEST_SECRET_KEY_BASE in Bitwarden; re-run scripts/deploy-finnest.sh --dry-run; confirm new value is fetched (bws picks up the update)

References