Legal release (Privacy, Terms, changelog)¶
Critical process. A legal update must never require hunting four places again.
Accuracy first: follow LEGAL_RELEASE_SOP.md (triggers, opsec rule, draft language, founder review, related-surface sweep). This document is the ship checklist after Ryan approves the language.
Use both documents for every Privacy Policy, Terms of Service, or public changelog change.
Before you edit anything¶
Read LEGAL_RELEASE_SOP.md, then the banner in content/legal/README.md. Do not ship until Ryan has approved the language.
Single source of truth (edit only here in last.vet-web):
| Asset | Path |
|---|---|
| Changelog JSON | content/legal-changelog.json |
| Current Privacy Policy | LASTVET_PRIVACY_POLICY.md |
| Current Terms of Service | LASTVET_TERMS_OF_SERVICE.md |
| Prior version archives | content/legal/privacy-vX.Y.md, content/legal/terms-vX.Y.md |
Never edit by hand:
| Copy | Why |
|---|---|
last.vet-api/src/content/legal-changelog.json |
Generated by npm run legal:sync-api |
| Live HTML on last.vet / my.last.vet | Deployed from this repo |
| Privacy notice email body | Rendered from API copy of the same JSON |
Release checklist (in order)¶
1. Edit content (this repo only)¶
- [ ] Update
content/legal-changelog.json(new entry at top per document) - [ ] Update
LASTVET_PRIVACY_POLICY.mdand/orLASTVET_TERMS_OF_SERVICE.md - [ ] Version and Effective Date headers in markdown must match the new changelog entry
- [ ] Archive prior markdown to
content/legal/privacy-vX.Y.md/terms-vX.Y.mdif superseding - [ ] Archive routes under
app/(site)/privacy/archive/andterms/archive/if new archive pages are needed
2. Validate and sync locally¶
From last.vet-web:
This runs validation and copies legal-changelog.json to ../last.vet-api.
3. Commit and push last.vet-web → main¶
- [ ] Push triggers last.vet (Cloudflare Git on
last-vet-web) - [ ] Push triggers my.last.vet (
.github/workflows/deploy-my-last-vet.ymlonlast-vet-my) - [ ] CI runs
npm run legal:validateon legal-touched PRs - [ ] CI runs
npm run legal:smokeon both domains after deploy
4. Commit and push last.vet-api (if JSON changed)¶
- [ ] Push so privacy notice emails use the same changelog copy
- [ ] Redeploy
last.vet-apion R940 (ssh -A lastvet-apithen/opt/lastvet-gates/scripts/deploy-lastvet-api.sh --prod-onlyaftermainis pulled) so privacy notice emails use the new JSON. Do not use the retired ECS script.
5. Verify live (all three public surfaces)¶
Wait until both Pages projects have finished deploying (last-vet-web via Cloudflare Git, last-vet-my via deploy-my-last-vet.yml). Then:
Confirm by hand if smoke is not enough:
- https://last.vet/privacy and
/terms(new version header; stale claims gone) - https://my.last.vet/privacy and
/terms(same new version; this host serves its own copy) - https://provider.last.vet footer Privacy/Terms links →
https://last.vet/privacyand/terms(canonical; no third copy) - https://last.vet/legal/changelog and https://my.last.vet/legal/changelog
- Prior-version archives still show the old text
Do not send the privacy-notice email until every surface matches. A current last.vet and a stale my.last.vet is a failed release.
6. Send material-change notice (if the changelog entry is material)¶
After live surfaces match:
- Preview:
GET /api/internal/privacy-notices/<version>/previewon the prod API (internal Bearer). - Send:
POST /api/internal/privacy-notices/<version>/sendwithincludeWaitlist: truefor material processor/PHI-location changes (same campaign as v1.1). - In-app: iOS and provider portal link to
https://last.vet/privacy//terms. They do not host a separate policy body. Once last.vet is the new version, those in-app links are the new version. There is no separate inbox-writer for legal notices; the email campaign is the push channel.
What deploys where¶
| Surface | Cloudflare / runtime | Deploy trigger |
|---|---|---|
| https://last.vet | Pages project last-vet-web |
Cloudflare Git build on push to main |
| https://my.last.vet | Pages project last-vet-my |
GitHub Actions deploy-my-last-vet.yml on push to main |
| https://provider.last.vet | last.vet-provider |
Links to last.vet legal URLs (no policy copy to deploy) |
| Privacy notice email | last.vet-api on R940 |
/opt/lastvet-gates/scripts/deploy-lastvet-api.sh after JSON sync |
Secrets (Bitwarden + GitHub)¶
Cloudflare deploy credentials are shared with onset-web (same Last 1 Enterprises account).
| Secret | Bitwarden SM (lastvet project) |
GitHub (Last-1-Enterprises/last.vet-web) |
|---|---|---|
CLOUDFLARE_API_TOKEN |
Yes | Yes (Actions) |
CLOUDFLARE_ACCOUNT_ID |
Yes | Yes (Actions) |
Refresh GitHub secrets from Bitwarden:
Manifest: lastvet-gates/bitwarden/secrets-manifest.json
CI workflows¶
| Workflow | When | Purpose |
|---|---|---|
| Validate legal content | PR / push touching legal paths | Blocks markdown ↔ changelog drift |
| Deploy my.last.vet | Every push to main |
Builds + deploys last-vet-my, smoke-tests both domains |
npm scripts¶
| Command | Purpose |
|---|---|
npm run legal:validate |
Fail if markdown headers ≠ changelog JSON (and API copy if sibling repo present) |
npm run legal:sync-api |
Copy JSON to last.vet-api |
npm run legal:smoke |
Live HTTP check: last.vet + my.last.vet versions, provider.last.vet canonical links |
npm run legal:release |
validate + sync-api |
Every npm run build and npm run build:my runs validation first.
Optional: auto-sync JSON to API via CI¶
Add GitHub secret LAST1_REPO_PAT (write access to last.vet-api) to enable future automation. Until then, npm run legal:release + manual API push is required.
Incident: email went out but website looked stale¶
Usually my.last.vet was not redeployed (separate Pages project). last.vet may already be current. Always run npm run legal:smoke after a legal release.