Security & maintenance¶
Secrets¶
| Secret | Purpose |
|---|---|
COVE_API_USER / COVE_API_PASSWORD |
Cove API login (read-only user) |
TOKEN_ENC_KEY |
Encrypts the stored M365 refresh token at rest |
CF_ACCESS_TEAM_DOMAIN / CF_ACCESS_AUD |
Verifying the Cloudflare Access token |
There is no Microsoft Graph client secret (delegated OAuth + PKCE). Secrets are set with wrangler secret put and are never committed. Tokens and the Cove visa are never logged.
Access control¶
- Every request is checked against the Cloudflare Access token server-side.
/adminis restricted twice: an Access policy on the path, and anadminEmailscheck in the Worker.- Reports can only be sent from the pinned shared mailbox, enforced in code.
Email deliverability (verify before go-live)¶
Confirm the sending domain is set up so reports aren't marked as spam or spoofable:
- SPF — your domain's SPF record must include Microsoft 365 (
include:spf.protection.outlook.com). - DKIM — in the Microsoft 365 Defender portal → Email & collaboration → Policies & rules → Threat policies → Email authentication settings → DKIM, enable DKIM signing for the domain (publish the two CNAMEs it gives you).
- DMARC — publish a DMARC TXT record at
_dmarc.<domain>, e.g.v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain. - Verify — send a report with Send test to me, open the email's "Show original"/headers, and confirm
SPF=pass,DKIM=pass,DMARC=pass.
Set a sensible Reply-To on the shared mailbox so replies reach a monitored inbox.
Monitoring & failures¶
- Failed scheduled runs alert admins by email and show a banner on the dashboard.
- History records every run with its error message; Admin → Audit log records who did what (connect, run, add/remove admin, schedule/recipient changes).
- Server logs are structured JSON (
{ level, message, timestamp, ... }) — view them withnpx wrangler tailor Cloudflare Workers Logs. Secrets and tokens are never logged. - Watch for a Reconnect Microsoft 365 prompt — it means the M365 token needs re-consent.
Backups¶
-
Database (D1): export periodically. From the project folder:
npx wrangler d1 export cove_audit --remote --output backup-$(date +%Y%m%d).sqlStore the file somewhere safe. To restore into a fresh database:
npx wrangler d1 execute cove_audit --remote --file backup-YYYYMMDD.sql. - Report archive (R2): each run's PDF + xlsx live in thecove-audit-archivebucket and are downloadable from History.
Data retention¶
The app prunes its own data automatically. In Admin → Data retention, set Keep for (months) (default 24; the config default is report.retentionMonths). Each day the cron:
- deletes
report_runsand their archived R2 files older than the retention period, - prunes
admin_actions(audit log) older than the retention period.
Admins can also click Run cleanup now for an immediate prune. Retention is clamped to 1–120 months. For a defence-in-depth backstop you can additionally add an R2 lifecycle rule on the bucket, but it's optional now that the app cleans up.
Rotating TOKEN_ENC_KEY¶
This key encrypts the stored M365 refresh token. Rotating it invalidates the stored token, so plan a reconnect:
- Generate a new key:
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))". - Set it:
npx wrangler secret put TOKEN_ENC_KEY(paste the new value). npm run deploy.- Go to Admin and click Reconnect Microsoft 365 — the new token is stored under the new key.
Do this on a schedule (e.g. yearly) or immediately if the key is ever exposed.
Routine maintenance¶
- Keep dependencies patched; run
npm auditand bump pinned versions deliberately. - CI (GitHub Actions) runs typecheck, lint, unit tests, and build on every push/PR.
- Run integration tests (
npm run test:int) and the E2E flow (npm run test:e2e) before big changes.
Updating the app¶
- Make changes and run
npm run lint,npm test,npm run typecheck. - Apply any new migrations:
npm run db:migrate:remote. - Deploy:
npm run deploy.