Story-2859

Wrapper-token mint + session+matter-gated open

RFD-AGT-013 T3b-1 / D18 (founder Q1). When counsel asks Mira for a document on WhatsApp, the brain never mints a URL — it resolves the document (Story-2855), and the broker issues a wrapper link on PRAMAAN's own domain: https://download.in.pramaan.click/<token>, alive ~24 hours. Opening it requires a normal PRAMAAN login plus the opener's own matter access; every open — the first or the hundredth — is audit-logged. Phone possession alone gets conversation, never documents.

Zero new migrations — the store was seeded

business.channel_fetch_tokens landed in the Story-2847 schema seed (migration 0207) with firm-scoped RLS, exactly so every W3 behavior ticket ships with no parallel alembic head. This story adds only code: the mint/open service, the serving Lambda, audit vocabulary, and three settings.

Mint — gate first, hash at rest

mint_fetch_link (src/domains/channel/fetch_link.py) runs on the caller's firm-scoped RLS session, in this order:

Open — the D18 gate, per open

open_fetch_link is the data path of the new src/download_open/handler.py Lambda, deployed over the Chore-2852 shell (pramaan-download-staging, auth-NONE at the gateway by design — the gate is handler logic). Per open:

Deploy + proof

CheckWhere
CC-37 restricted mints nothing; hash-only at rest; mint audited tests/integration/test_channel_fetch_link.py
CC-19/21/36 multi-open: 2 opens → 2 unique watermarked PDFs + use_count=2 + 2 audit rows tests/integration/test_channel_fetch_link.py
CC-39 denials: no matter access / non-member; expiry, revocation, epoch each kill the link, all audited tests/integration/test_channel_fetch_link.py
Wire contract: 401 / uniform 404 / 302 no-store / canary sentinel tests/unit/test_download_open_handler.py
Token entropy + hash form + base-URL registry resolution tests/unit/test_channel_fetch_link_helpers.py
Deploy contract: code-only, no aws iam, canary pinned to the handler sentinel (R4/R6) tests/unit/test_download_open_deploy_workflow.py

The deploy (.github/workflows/deploy-download-open.yml) updates code on the Terraform shell only. Its canary drives the real cold-start dependency set — the ADR-162 DB SecureString read, the base-url registry read, the pinned Clerk key, and Story-2861's transient watermark S3 round trip — and goes RED while the relevant pramaan-infra twin is unapplied: the R2 contract, observed red before it is believed green. The G5b W3 LIVE probe runs after that twin lands.