From 7c6bf101018c32a0e287eeaa97fe485ba80a956d Mon Sep 17 00:00:00 2001 From: Liz Sweigart <127434495+NotThatKindOfDrLiz@users.noreply.github.com> Date: Thu, 14 May 2026 19:44:17 -0500 Subject: [PATCH] docs: align contributor guidelines with divine-mobile Rewrite AGENTS.md in the section structure used in the divine-mobile repo (Repo Shape, Worktree-First Workflow, PR Guardrails, No Tech Debt, Architecture, OAuth/Signing/Identity Rules, Verification, Database, Secrets/Deployment, Clean Workspace), adapted to keycast's Rust + SvelteKit stack and actual commands. Add a new CONTRIBUTING.md modeled on divine-mobile's outside-contributor guide, covering contribution status, good first contributions, templates and agent compliance, the technical-debt standard, why PRs get closed, repo setup, the worktree-first workflow, where to work across crates and web, architecture and security boundaries, day-to-day commands, testing, scope discipline, the PR checklist, secrets, and docs rules. Co-Authored-By: Claude Opus 4.7 (1M context) --- AGENTS.md | 129 +++++++++++----- CONTRIBUTING.md | 397 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 489 insertions(+), 37 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/AGENTS.md b/AGENTS.md index 4e3927e6..ec549980 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,42 +1,97 @@ # Repository Guidelines -## Project Structure & Module Organization -- This repo is a Rust workspace with core crates under `api/`, `core/`, `signer/`, `cluster-hashring/`, and the unified binary under `keycast/`. -- Web assets live under `web/` and use SvelteKit with Bun-based workflows. -- Database migrations live under `database/migrations/`. -- End-to-end and integration coverage lives in `e2e/` and `tests/`. -- Operational and design notes live in `docs/`. - -## Build, Test, and Development Commands -- `bun run dev`: run the unified binary locally with hot reload. -- `bun run dev:web`: run the web frontend locally. -- `cargo test --workspace --verbose`: run workspace Rust tests. -- `cargo clippy --workspace --all-targets --all-features -- -D warnings -A deprecated`: match CI lint expectations. -- `cargo fmt --all -- --check`: verify formatting before review. -- Use targeted test commands when a change is scoped to one crate or one e2e path, but record that scope clearly in the PR. - -## Coding Style & Naming Conventions -- Keep Rust changes idiomatic and formatted with `rustfmt`. -- Keep frontend changes aligned with the current SvelteKit/Bun setup rather than introducing new tooling casually. -- Prefer small, task-scoped changes over wide refactors. - -## Commit & Pull Request Guidelines -- PR titles must use Conventional Commit format: `type(scope): summary` or `type: summary`. -- Set the correct PR title when opening the PR instead of relying on title edits afterward. -- If a PR title must be fixed after opening, verify that the semantic PR title check reruns successfully. -- Keep PRs tightly scoped to the task. Do not include unrelated formatting churn, lockfile noise, drive-by refactors, or incidental cleanup. -- Temporary or transitional code must include `TODO(#issue):` with a tracking issue for later removal. -- PR descriptions should include a concise summary, motivation, linked issue, and manual test plan. -- For `web/` or other UI-facing changes, include screenshots/video in the PR or explicitly state that there is no visual change. -- Do not mention corporate partners, customers, brands, campaign names, or other sensitive external identities in public issue titles, PR titles, branch names, screenshots, or descriptions unless a maintainer explicitly approves it. Use generic descriptors instead, such as "partner account", "creator page", or "external partner". +## Repo Shape And Source Of Truth + +- This is a Rust workspace plus a SvelteKit frontend. The unified server binary is `keycast/src/main.rs`; HTTP routes live in `api/`, shared business logic in `core/`, the NIP-46 signer in `signer/`, and Redis-backed cluster coordination in `cluster-hashring/`. +- The web app lives in `web/` and uses SvelteKit with Bun for package management. +- Database migrations live in `database/migrations/`. End-to-end and integration coverage lives in `e2e/` and `tests/`. +- Operational and design notes live in `docs/` (start with `ARCHITECTURE.md`, `DEVELOPMENT.md`, `DEPLOYMENT.md`, `SECURITY.md`, and the OAuth/signer-specific guides). `CLAUDE.md` is also kept current and is the fastest orientation read. +- Older docs can drift. If documentation conflicts, trust the current implementation, targeted tests, and the newest focused doc over historical notes. + +## Worktree-First Task Workflow + +- Start every new task in a **new worktree branched from `origin/main`** — never from local `main` (often stale), never from another branch or worktree. +- Fetch first, then create the worktree: + - `git fetch origin` + - `git worktree add .worktrees/ -b origin/main` +- Keep one task per worktree. Do not mix unrelated fixes, reviews, or experiments in the same tree. +- If the current checkout is dirty, do not start new work there. Commit it, stash it intentionally, or discard it intentionally first. +- **Rebase onto fresh `origin/main` before every push**, even on a branch you've already pushed: + - `git fetch origin && git rebase origin/main` + - `git push --force-with-lease` (never `--force` without `--lease`) +- Never merge `main` into a feature branch — always rebase. + +## PR Guardrails + +- Every PR title must use Conventional Commit format: `type(scope): summary` or `docs: summary` for docs-only PRs. The semantic PR check (`.github/workflows/semantic_pr.yml`) enforces this. +- Set the semantic title when creating the PR. Do not rely on editing it afterward; if you must, verify the semantic check reruns successfully. +- **Every PR targets `main`. Never stack PRs.** When features are interdependent, ship them as **one combined PR** with clearly delineated commits and a description that calls out each feature separately. Never `gh pr create --base `. +- A task is not complete if the intended changes are still uncommitted. +- Stage only the files that belong to the task. Avoid broad staging when the worktree contains unrelated changes. +- End each task with a clean `git status` except for changes that are explicitly still in progress and clearly called out. +- Open a pull request once the change is ready for review. Do not leave finished work sitting only in a local branch or worktree. +- Use `.github/pull_request_template.md` and fill out summary, motivation, related issue, testing, and visuals sections. +- For `web/` or other UI-facing changes, attach screenshots/video or explicitly state that there is no visual change. +- Do not name corporate partners, customers, brands, or campaign names in public issue titles, PR titles, branch names, screenshots, or descriptions unless a maintainer explicitly approves it. Use generic descriptors such as "partner account", "creator page", or "external partner". + +## No Technical Debt, No Failing Tests + +- Do not accumulate technical debt. Fix issues in the PR that touches them; do not defer with TODOs, follow-up issues, skipped tests, or commented-out code. The only acceptable TODO is a transitional-code TODO with a tracking-issue link: `TODO(#issue): ...`. +- **`origin/main` always passes.** Any failing test on a feature branch is caused by that branch's diff. Never claim flakiness, never `#[ignore]` to silence a failure, never push red "to see what CI says." Run the affected targeted tests plus `cargo fmt --all -- --check` and `cargo clippy --workspace --all-targets --all-features -- -D warnings -A deprecated` before every push. - Do not continue speculative feature work after exploratory implementation if maintainer alignment on scope or UX is still missing. -## Testing Expectations -- Run `cargo test --workspace --verbose`, `cargo clippy --workspace --all-targets --all-features -- -D warnings -A deprecated`, and `cargo fmt --all -- --check` before requesting review. -- When touching OAuth, auth/session behavior, NIP-05 behavior, or signer flows, run the most relevant targeted tests and document which ones were used. -- When changing `web/` behavior, manually verify the affected path and document the manual checks in the PR. +## Architecture And Layering + +- HTTP handlers in `api/` should stay thin. Push business logic into `core/` so it stays testable and reusable from the signer, the HTTP RPC path, and tests. +- Treat `core/` as the source of truth for database models, encryption, UCAN/session handling, OAuth state, and the custom permissions trait. +- Encrypted secrets (stored keys, OAuth keypairs, master key material) only ever leave `core/`'s key-manager abstractions when actively in use. Do not log, serialize, or persist plaintext key material. +- New custom permissions implement `CustomPermission` (`core/src/traits.rs`) and must be registered in both `core/src/custom_permissions/mod.rs` (`AVAILABLE_PERMISSIONS`), `core/src/types/permission.rs` (`to_custom_permission()`), and `web/src/lib/types.ts` (`AVAILABLE_PERMISSIONS`). +- The signer routes incoming NIP-46 requests by recipient pubkey; preserve that contract when adding handlers and avoid global state that conflates authorizations. +- Keep frontend changes aligned with the existing SvelteKit/Bun setup. Reuse existing components and stores rather than adding parallel patterns. + +## OAuth, Signing, And Identity Rules + +- Treat OAuth client configuration, session handling, UCAN issuance, relay configuration, and production identity settings as sensitive operational context. Call out changes that affect them explicitly in the PR body. +- Authentication uses UCAN tokens (Bearer or `keycast_session` cookie). Do not introduce parallel auth schemes; extend the UCAN path instead. +- OAuth authorizations support multi-device — each approval creates a new authorization and revocation is soft-delete via `revoked_at`. Preserve that semantic when touching authorization lifecycle code. +- Server-side keys are encrypted at rest with the configured `KMS_PROVIDER` (`file`, `gcp`, or `aws`). Do not bypass `core/`'s key-manager abstractions. +- Never truncate Nostr pubkeys, event IDs, or signatures in logs, error messages, analytics, or test fixtures. Use full values and let UI handle overflow. + +## Verification + +Run the smallest relevant verification first, then broaden if the change is cross-cutting. + +- Format and lint: + - `cargo fmt --all -- --check` + - `cargo clippy --workspace --all-targets --all-features -- -D warnings -A deprecated` +- Rust tests: + - `cargo test --workspace --verbose` for a quick pass. + - `bun run test` to spin up Postgres + Redis via `docker-compose.deps.yml`, set up the test database, and run the full workspace + integration-feature test suite (matches what CI runs via `bun run test:ci`). + - Targeted test commands (e.g. `cd api && cargo test --test oauth_integration_test`) when a change is scoped to a single crate or path. Record that scope in the PR. +- Combined gate: `bun run check` runs fmt, clippy, and `cargo test --workspace` together. +- Optional pre-push parity with CI: `bun run setup:hooks` installs `scripts/hooks/pre-push`. +- For `web/` changes, manually verify the affected path in the browser and document the manual checks in the PR. + +When touching OAuth, auth/session behavior, NIP-05/profile behavior, signer flows, encryption, or cluster coordination, run the most relevant targeted tests and document which ones were used. + +## Database And Migrations + +- New schema changes live in `database/migrations/` as a new timestamped migration. Do not edit shipped migrations. +- Use SQLx for queries so compile-time verification stays meaningful. If you change a query, regenerate `sqlx-data.json` (where applicable) and commit it with the source change. +- Locally, `bun run db:reset` recreates the dev database; `bun run db:migrate` applies new migrations. Production migrations are run via `tools/run-migrations.sh` from the deploy pipeline. + +## Secrets, Local Stack, And Deployment + +- Do not commit secrets, real credentials, master keys, or production `.env` files. The dev master key is generated locally via `bun run key:generate`. +- Local development uses `docker-compose.deps.yml` for Postgres and Redis. The dev `SERVER_NSEC` and `ALLOWED_PUBKEYS` values in `package.json` scripts are intentionally non-secret development values; do not reuse them for any deployed environment. +- Production runs on Google Cloud Run as service `keycast` (us-central1) with `min-instances=3` so the NIP-46 signer stays connected. Be careful with changes that affect startup, signer connection lifecycle, or Redis/cluster coordination — those have outsized blast radius. +- Deploys are gated behind `bun run deploy` (Cloud Build). Do not deploy on someone else's behalf without explicit confirmation. + +## Clean Workspace Expectations -## Security & Deployment Notes -- Do not commit secrets or real credentials. -- Treat OAuth client configuration, session handling, relay configuration, and production identity settings as sensitive operational context. -- Be careful with changes that affect signing, token issuance, encryption, or multi-tenant identity semantics; call those out explicitly in the PR body. +- Do not leave untracked or modified files around after a task unless they are part of the intentional diff. +- Delete temporary debugging artifacts (scratch scripts, throwaway logs, ad-hoc fixtures) before commit. +- If a generated file must be committed, make sure it is reproducible and relevant to the change. +- Before opening the PR, review the diff and remove stray edits, generated junk, logs, scratch files, and half-finished experiments. +- After opening or updating a PR, inspect GitHub checks and rerun stale semantic jobs if needed. +- After a branch is merged or abandoned, prune the worktree and branch so stale task state does not accumulate. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..2c62e4e9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,397 @@ +# Contributing To Keycast + +Status: Current +Validated against: `AGENTS.md`, `CLAUDE.md`, `package.json` scripts, `.github/` +templates and workflows, and current workspace layout on 2026-05-14. + +This guide is the source of truth for outside contributors. + +## Current Contribution Status + +We appreciate the time and care people put into this project. Right now, +however, we are **not accepting outside implementation PRs by default** while +we tighten our contributor guidance and clarify product ownership boundaries +around OAuth, signer behavior, and tenant identity. + +What this means in practice: + +- A well-intentioned PR may still be closed if it lands in an area where + product, UX, security, or sequencing is not explicitly approved. +- Large feature branches are especially likely to be closed even if they + contain real effort and test coverage, because the review and cleanup cost is + too high for the current team size. +- We are working toward clearer issue labels and contribution lanes. Until that + is in place, assume implementation work needs maintainer buy-in **before** + code is written. + +If you want to help right now, the safest path is to start with issues that a +maintainer has explicitly marked as ready for outside contribution. + +## Good First Contributions + +These are the kinds of changes most likely to be reviewable and mergeable: + +- Small bug fixes with a clear reproduction and narrow diff. +- Targeted tests that improve coverage around existing behavior — especially + around OAuth, UCAN sessions, the NIP-46 signer, and custom permissions. +- Documentation fixes that align `docs/`, `README.md`, or `CLAUDE.md` with + current code. +- Small refactors requested by a maintainer. +- Clearly scoped UI polish in `web/` where the intended behavior and visual + direction are already settled. + +These are **not** good first contributions unless a maintainer has explicitly +asked for them: + +- New features or feature revivals from old issues, designs, or dormant PRs. +- Broad UX or information architecture changes in `web/`. +- New auth flows, token formats, session models, or changes to how UCANs are + issued or validated. +- New storage, encryption, key-manager, or cluster coordination patterns. +- New custom permission types without a maintainer-confirmed shape. +- "I implemented the whole issue" PRs opened without prior maintainer + confirmation. + +## Before You Start + +Before writing code, make sure the answer to all of these is yes: + +1. A maintainer has indicated the work is wanted **now**, not just eventually. +2. The intended product behavior and security posture are already clear. +3. The change can be delivered as a small, focused PR. +4. You know which crate or layer owns the change (`api/`, `core/`, `signer/`, + `cluster-hashring/`, `keycast/`, or `web/`). +5. You are prepared to run the relevant tests and migrations locally. + +If any of those are unclear, start a discussion first instead of opening a PR. + +## Templates And Agent Compliance + +We enforce our repository templates and agent instructions. + +Before starting work: + +- Read [AGENTS.md](AGENTS.md). +- Read [CLAUDE.md](CLAUDE.md) for the architectural overview, environment + variables, and operational context. +- If you use Codex, Claude, ChatGPT, Cursor, Copilot, or any other coding + agent, make sure that agent has reviewed those files before it writes code, + opens an issue, or opens a PR. + +When opening issues and PRs: + +- Use the appropriate GitHub issue template under + [.github/ISSUE_TEMPLATE](.github/ISSUE_TEMPLATE) and fill it out completely. +- Follow the issue title prefixes from the templates: `fix:` for bug reports + and `feat:` for feature requests. +- Use the PR template in [.github/pull_request_template.md](.github/pull_request_template.md). +- Use a semantic PR title that matches our checks. The repo enforces this via + [.github/workflows/semantic_pr.yml](.github/workflows/semantic_pr.yml). + +Submissions that ignore the templates, semantic formatting, or repo +instructions may be closed or sent back for correction before review starts. + +## Technical Debt Standard + +In the age of agentic programming, we expect a higher bar, not a lower one. +We enforce a standard of: + +- No new technical debt in submitted PRs. +- Elimination of as much relevant prior technical debt as is reasonably + possible within the scope of the change. + +That means: + +- Do not add TODOs, compatibility shims, temporary hacks, commented-out code, + partial migrations, or "we'll fix this later" scaffolding unless a maintainer + has explicitly approved a transitional step. The only acceptable TODO is + `TODO(#issue): ...` with a tracking-issue link. +- Do not leave migrations half-applied, queries with stale `sqlx-data.json`, + tests red, or known cleanup deferred just to get the branch over the line. +- If your change touches an area with existing debt, clean up the parts you are + already in unless doing so would turn the PR into a different project. +- If a proposed fix would require introducing new mess to ship now and repair + later, stop and rescope the change instead. + +Using an agent is not an excuse to produce larger diffs with lower standards. +If anything, agent assistance means we expect contributors to arrive with +cleaner structure, better test coverage, stronger adherence to repo rules, and +less leftover cleanup for maintainers. + +## Why PRs Get Closed + +We want to be direct about this because it saves everyone time. + +A PR may be closed without merge if any of the following are true: + +- The work was started without maintainer alignment. +- The feature direction is still unsettled across product, security, or + architecture. +- The branch is too large or spans too many concerns to review safely. +- The implementation solves the wrong problem, even if the code quality is + solid. +- The branch mixes feature work with unrelated cleanup, dependency churn, or + architecture experiments. +- The branch introduces new technical debt or avoids cleanup that should have + been handled in the touched area. +- The change touches OAuth, signing, encryption, UCAN issuance, or + multi-tenant identity without explicit maintainer approval. +- The change creates more review, rework, or coordination cost than the team + can absorb right now. + +Closure in those cases is usually a sequencing decision, not a judgment on +effort or intent. + +## Repository Setup + +Prerequisites: + +- Rust toolchain pinned by `rust-toolchain.toml` (installed automatically by + rustup). +- [Bun](https://bun.sh/) for the web frontend and the npm-style scripts in the + root `package.json`. +- Docker (for local Postgres and Redis via `docker-compose.deps.yml`). +- `sqlx-cli` for migrations (`cargo install sqlx-cli --no-default-features --features rustls,postgres`). + +Initial setup: + +```bash +git clone https://github.com//keycast.git +cd keycast +bun install +bun run key:generate # generates ./master.key for local dev +bun run deps:up # starts Postgres + Redis containers +bun run db:reset # creates the dev database and runs migrations +``` + +Optional but recommended: + +```bash +bun run setup:hooks # installs the pre-push hook (fmt + clippy + tests) +``` + +## Worktree-First Workflow + +Start every task from a fresh branch created from `origin/main`: + +```bash +git fetch origin +git worktree add .worktrees/ -b origin/main +``` + +Rules: + +- Never start new work in a dirty checkout. +- Keep one task per worktree. +- Rebase onto fresh `origin/main` before every push. +- Never merge `main` into a feature branch. Always rebase. +- Never stack PRs. Every PR targets `main`. + +## Where To Work + +- **`api/`** — HTTP routes, request/response shapes, middleware. Keep handlers + thin; push logic into `core/`. +- **`core/`** — Database models, encryption, key managers, UCAN/session + handling, OAuth state, custom permissions trait. Source of truth for + business logic. +- **`signer/`** — NIP-46 signer that handles bunker connections and routes + requests to the right authorization. +- **`cluster-hashring/`** — Redis-backed cluster coordination (Pub/Sub + + consistent hashing) used to balance signer ownership across instances. +- **`keycast/`** — The unified binary that runs the API and signer in a single + process. +- **`web/`** — SvelteKit frontend. +- **`database/migrations/`** — Add new timestamped migrations here. Never edit + shipped migrations. +- **`docs/`** — Architectural and operational notes. Start with + [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md), + [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md), [docs/SECURITY.md](docs/SECURITY.md), + and [CLAUDE.md](CLAUDE.md). + +Trust current implementation and focused tests over stale historical docs. + +## Architecture Expectations + +- Keep the layering `HTTP route -> core service -> database/key manager`. Do + not call the database directly from `api/` handlers. +- Treat encrypted secrets as untouchable outside `core/`'s key-manager + abstractions. Do not log, serialize, or persist plaintext key material. +- Authentication is UCAN-based (Bearer token or `keycast_session` cookie). Do + not introduce parallel auth schemes; extend the UCAN path instead. +- OAuth authorizations are multi-device: each approval creates a new + authorization row, and revocation is soft-delete via `revoked_at`. Preserve + that contract. +- New custom permissions implement `CustomPermission` (`core/src/traits.rs`) + and must be registered in three places (see [CLAUDE.md](CLAUDE.md) for the + full checklist). +- Reuse existing SvelteKit components and stores in `web/src/lib/` rather than + introducing parallel patterns. + +## Product, Security, And Design Boundaries + +Do not assume that an old issue, mockup, comment thread, or dormant branch is +enough authorization to implement a feature. For contribution purposes, the +source of truth is current maintainer direction. + +If the change affects any of the following, get explicit maintainer +confirmation first: + +- OAuth flow shape, scope semantics, or token issuance. +- UCAN issuance, session cookie handling, or `SERVER_NSEC` usage. +- Encryption, key managers, or master key handling. +- NIP-46 signer connection lifecycle, relay configuration, or request routing. +- Cluster coordination behavior in `cluster-hashring/`. +- New storage models, table additions, or migrations beyond a single column + change. +- New custom permission types. +- Multi-tenant identity behavior (NIP-05, profile, `/.well-known/nostr.json`). +- UI flows that need design fidelity rather than engineering approximation. + +If direction is unresolved, stop and ask first. Do not fill in the blanks +yourself and hope review will sort it out later. + +## Day-To-Day Commands + +Local dev (API + signer + web concurrently): + +```bash +bun run dev # http://localhost:3000 (API + signer) +bun run dev:web # https://localhost:5173 (web frontend) +``` + +Build: + +```bash +bun run build # cargo build --release --bin keycast +bun run build:web # SvelteKit production build into web/build/ +``` + +Database: + +```bash +bun run db:reset # drop, recreate, and migrate the dev database +bun run db:migrate # apply new migrations +``` + +Logs from production Cloud Run: + +```bash +bun run logs # recent logs +bun run logs:watch # streaming +``` + +## Testing Expectations + +Start with the smallest relevant verification, then broaden when the diff is +cross-cutting. + +Core checks: + +```bash +cargo fmt --all -- --check +cargo clippy --workspace --all-targets --all-features -- -D warnings -A deprecated +cargo test --workspace --verbose +``` + +Combined gate (matches what the pre-push hook runs): + +```bash +bun run check +``` + +Full test suite with Postgres + Redis brought up via Docker (matches +`bun run test:ci`): + +```bash +bun run test +``` + +Targeted examples: + +```bash +cd api && cargo test --test oauth_integration_test +cd api && cargo test --test oauth_unit_test +``` + +Additional expectations: + +- Add or update tests next to the changed crate or feature. +- Do not push red tests "to see what CI says." +- For `web/` changes, manually verify the affected path in the browser and + document the manual checks in the PR. +- When touching OAuth, UCAN/session behavior, NIP-05/profile behavior, signer + flows, encryption, or cluster coordination, run the most relevant targeted + tests and document which ones were used. + +## Scope Discipline + +Keep PRs focused and reviewable. + +Do: + +- Change only what is required for the agreed task. +- Stage only task-related files. +- Keep dependency changes justified and narrow. +- Split truly independent work into separate PRs targeting `main`. + +Do not: + +- Mix feature work with unrelated cleanup or version bumps. +- Add speculative architecture while solving a smaller problem. +- Land partial work with TODOs instead of finishing or scoping down. +- Introduce new technical debt with the expectation that maintainers will clean + it up later. +- Leave the branch dirty at handoff. + +## Pull Requests + +Requirements: + +- Use a Conventional Commit PR title such as + `feat(api): add per-app revocation endpoint` or + `fix(signer): drop stale relay subscriptions on reconnect`. +- Make sure the PR title is semantic when the PR is opened. Editing the title + later does not reliably retrigger the semantic check. +- Target `main`. +- Fill out [.github/pull_request_template.md](.github/pull_request_template.md) + completely, including summary, motivation, related issue, testing checklist, + and visuals section. +- Include a clear description of what changed, what is out of scope, and how + you verified it. For changes that touch OAuth, signing, encryption, UCAN, or + multi-tenant identity, call that out explicitly. +- End with a clean `git status`. +- Rebase on `origin/main` before pushing. + +Before opening a PR, ask yourself: + +1. Is this branch small enough for a maintainer to review without + reconstructing product intent? +2. Does it stay inside a clearly approved scope? +3. Did I avoid mixing in unrelated files or concerns? +4. Did I run the relevant tests locally? +5. Did I avoid naming corporate partners, customers, or sensitive external + brands in the title, branch name, screenshots, or description? + +If the answer to any of those is no, the PR is not ready yet. + +## Secrets And Sensitive Data + +- Never commit secrets, real credentials, master keys, production `.env` + files, or live `SERVER_NSEC` values. +- The dev `SERVER_NSEC` and `ALLOWED_PUBKEYS` values in `package.json` scripts + are intentionally non-secret development values; do not reuse them for any + deployed environment. +- Never truncate Nostr pubkeys, event IDs, or signatures in logs, tests, or + analytics output. Use full values and let UI handle overflow visually. +- Do not include user-identifying or tenant-identifying data in test fixtures + unless the test specifically requires it. + +## Documentation Rules + +- Current architectural and operational docs belong in `docs/` (or `CLAUDE.md` + when the audience is coding agents). +- Historical plans and completed investigations should be preserved under + `docs/archive/` or clearly marked historical, not silently deleted. +- Before adding a new top-level doc, check whether the content fits inside an + existing doc instead. New docs should have a clear audience and a stated + status (Current vs. Historical) at the top.