#Best Practices
This directory collects best-practice patterns for writing Markdown-driven backend definitions. Every page is written strictly against docs/spec-v1.7.md, the current finalized spec. Section references such as "§7.2" or "Decision 48" point to that spec. The older docs/spec-v1.5.md is kept for historical reference only.
Spec version note: these pages were revised for spec v1.7 (issue #17). Main changes from the v1.5 edition: optimistic locking uses
ETag/If-Matchwith 428/412 (Decision 48); public registration takes no password and always returns 202 (Decisions 56, 67); login requires a verified email (Decision 55); admin user creation is an invite flow (Decision 69); API-key, JWT, rate-limit, refresh-token and LLM-provider settings follow the values v1.7 now specifies (Decisions 43–45, 57–59, 61, 62, 65, 72). If a page and the spec disagree, the spec wins.
Each page has the same structure:
- Purpose – what the pattern is for and when to use it.
- Natural-language source example – the
.mdfile a human writes (§2.2). - Compiled lock md example – the
{name}.lock.mdthe compiler produces (§2A). Lock files are generated by the compiler and must never be hand-written or hand-edited; the examples here are illustrative only. - Security considerations – the rules the deterministic validator enforces (§10) and what reviewers must check (§2A.5).
- Spec gaps – anything v1.7 leaves unspecified or states inconsistently. Where the spec does not define something, these pages say so instead of guessing.
The spec itself is written in Cantonese; source examples here are in English. Natural-language source can be written in any language; only the compiled lock is authoritative at runtime (§2.1).
#Conventions used in examples
- Approver names (
jack@example,mei@example), PR numbers (pr#121…) and commit ids in lock frontmatter are fictional, copied in style from the spec examples. - Hashes, signatures and commit ids are shortened placeholders (
sha256:9a1c...e2,MEUCIQ...), exactly as in the spec examples. - When a lock is shown as an excerpt ("sections only"), the frontmatter follows §2A.1 exactly; api locks additionally carry
scope:(§2A.3). - Sections not used by a lock are written as
(none)(§2A.2).
#Index
| Topic | Page |
|---|---|
| Auth pipeline – JWT (and admin invite via create-user) | auth-jwt.md |
| Auth pipeline – API key | auth-api-key.md |
| CRUD – offset pagination | crud-offset-pagination.md |
CRUD – optimistic locking (ETag/If-Match) |
crud-optimistic-locking.md |
| CRUD – soft delete | crud-soft-delete.md |
Registration (user.self-register, user.complete-verification, admin user.register) |
user-register.md |
user.login |
user-login.md |
sign-token (token issuance capability) |
sign-token.md |
| Refresh token rotation | refresh-token-rotation.md |
| Rate limiting | rate-limit.md |
| Public writes | public-writes.md |
| Privacy deletion (crypto-shredding + anonymization) | privacy-deletion.md |
| LLM provider setup (compiler endpoint) | llm-provider-setup.md |
#Cross-cutting rules (apply to every page)
- Runtime never interprets natural language; only approved, signed locks (via the signed manifest) are executed (§2.1, §9).
- Every endpoint goes through
authby default; removing it (-auth) requirespublic: true(§3, §4). - No
DELETEorTRUNCATESQL in any lock, and no DDL outsidekind: migrationlocks; everyUPDATEneeds aWHEREplusscope: pk | rls(§2A.4, §2B, Decisions 27, 51). - SQL is parameterized (
$1,$2+params), single-statement (§2A.2, §2A.4). - Natural-language locks and any lock touching a security item need manual approval; high-risk items need two non-author owners/maintainers; max 5 security items per PR across all its locks (§2A.5, Decisions 19, 33, 34).
- Row-level ownership inside a tenant uses
owner-key(Decision 70, §12.6a); RLS alone only isolates tenants. - Secrets (DB connections, JWT keys, LLM keys) appear only as references, never in source, lock, manifest or logs (§2A.8.5, §9B). Audit never records plaintext email (Decision 68).