Venn

Maintaining Venn

Notes for maintainers: how the repository is set up, the settings that live outside the code, and how changes get to production.

How changes land

  1. Every change, including the maintainer's own, goes through a pull request into main.
  2. CI must pass. Branch protection requires one check, CI passed, which only goes green when every CI job has passed. New jobs added to ci.yml are covered automatically.
  3. Review threads must be resolved.
  4. Pull requests are squash-merged, so main has one commit per change and stays linear.

With a single maintainer, no approving review is required, because GitHub doesn't let you approve your own pull request. When a second maintainer joins, raise Required approvals to 1 and turn on Require review from Code Owners.

What runs automatically

Workflow When What it does
ci.yml Every PR and push to main Lint, formatting, docs links and site build, actionlint, npm signature check, unit/API tests with a coverage floor (Node 22 + 24), browser and accessibility tests (desktop + phone), Worker build. Docs-only PRs skip the heavy jobs. See CI.md
ci.yml → Dependency review Every PR Blocks new dependencies with known vulnerabilities (moderate and up) or non-permissive licenses
codeql.yml Every PR, main, and weekly Static security analysis of the JavaScript and of the workflow files
claude-review.yml Every non-draft PR from this repo Advisory code review by Claude, guided by CLAUDE.md. Needs the ANTHROPIC_API_KEY secret
labeler.yml Every PR Labels the PR by area (app, server, docs, tests, ci, dependencies)
dependabot-auto-merge.yml Dependabot PRs Auto-merges patch and minor updates once CI passes
pages.yml Push to main Builds and publishes the website and docs to GitHub Pages
scorecard.yml Push to main, and weekly OpenSSF Scorecard security score (README badge)
deploy.yml Push to main Deploys to Cloudflare, only if CLOUDFLARE_DEPLOY=true is set (off by default)
Dependabot Weekly Grouped update PRs for npm dev tools and GitHub Actions, after a 3-day cooldown

All workflows start with read-only permissions. Pull requests from forks run with no access to secrets. Every third-party action is pinned to a full commit hash; Dependabot keeps the hashes current.

One-time repository settings

These can't be stored in the code, so set them by hand. They're listed in order.

1. Protect main

Settings → Rules → Rulesets → New ruleset → Import a ruleset, and choose .github/rulesets/protect-main.json. It:

2. General

Settings → General:

3. Actions

Settings → Actions → General:

4. Security

Settings → Advanced Security (named Code security on some accounts):

5. Deploy environment (only if you use deploy.yml)

Settings → Environments → New environment named Venn-App (the name deploy.yml uses):

6. GitHub Pages

Settings → Pages → Build and deployment → Source: GitHub Actions. pages.yml then publishes the product page and docs on every push to main, at https://<owner>.github.io/venn/.

The project's docs use the subdomain docs.venn.day (the app itself is on venn.day; see below). To set up a subdomain like it:

  1. Settings → Pages → Custom domain: enter docs.venn.day and save. Pages deployed by Actions don't need a CNAME file.
  2. Cloudflare → DNS: add CNAME docs → <owner>.github.io set to DNS only (grey cloud), so GitHub can issue the HTTPS certificate.
  3. GitHub → your account → Settings → Pages → Add a domain: verify venn.day with the TXT record it gives you. This stops anyone else's repository from claiming any of its subdomains.
  4. Once the certificate is issued, tick Enforce HTTPS under the repository's Pages settings.

The site's main button links to https://venn.day. Forks can point it at their own instance with the repository variable APP_URL.

7. Automated review

claude-review.yml reviews every non-draft pull request from this repository and leaves inline comments. It's advisory: it never blocks merging.

8. Dependabot auto-merge

dependabot-auto-merge.yml turns on auto-merge for Dependabot's patch and minor updates, so they merge once CI passed is green. This needs Allow auto-merge from step 2. Major updates wait for a person.

9. Community

Hosting a public instance

The app ships with sensible defaults: a strict Content Security Policy, no-referrer, no framing, JSON-only writes, request size limits and hashed secrets. When you run it on your own domain, also:

The official site: venn.day

The landing page, privacy page and app are all served by the one Worker. To set it up:

  1. Register the domain. In the Cloudflare dashboard, go to Domain Registration → Register Domains and buy venn.day. Registering it with Cloudflare adds it as a zone on the same account automatically. If you buy it elsewhere, add it as a site in Cloudflare and point the registrar's nameservers at the two Cloudflare gives you.
  2. Create the database: npx wrangler d1 create venn.
  3. Create an API token under My Profile → API Tokens with Account → Workers Scripts: Edit, Account → D1: Edit, Zone → Workers Routes: Edit and Zone → DNS: Edit (zone: venn.day).
  4. Set the repository's Actions settings: variables CLOUDFLARE_DEPLOY=true, D1_DATABASE_ID and CUSTOM_DOMAIN=venn.day; secrets CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID.
  5. Deploy: run the Deploy workflow, or merge to main. Wrangler attaches venn.day as a Custom Domain and creates its DNS record and certificate.
  6. Redirect www. Add a proxied DNS record for www (for example AAAA www 100::) and a Rules → Redirect Rules rule from www.venn.day/* to https://venn.day/${1} (301, keep the query string).
  7. Harden it: work through hosting a public instance below: HSTS, the rate-limiting rule and backups. The privacy page is already at /privacy; update it if you add another service that handles data.
  8. Optional extras: verify venn.day in Resend for notification emails (EMAIL_FROM="Venn <notifications@venn.day>"), and add https://venn.day/oauth-callback.html as a redirect URI for the Google and Microsoft calendar apps.

After changing the landing page's main screenshot, regenerate the social preview with npm run og.

Releases

Each merge to main is deployable. To mark a version, create a release from Releases → Draft a new release. Use a tag like v1.2.0 and let GitHub generate the notes from the merged pull requests.