# server notes ## stack - python - fastapi - pydantic - sqlite - uvicorn - pytest - docker ## env vars ```text PHONE_CAPTURE_TOKEN=change-me PHONE_CAPTURE_ORG_PATH=/data/synq.org PHONE_CAPTURE_DB_PATH=/data/capture.sqlite3 PHONE_CAPTURE_HOST=0.0.0.0 PHONE_CAPTURE_PORT=8765 ``` ## endpoints - `GET /health` - `POST /capture` see `docs/api.md`. ## file writing server appends to one org file. it does not parse or rewrite existing org content. recommended append behavior: - format entry into string ending with newline. - acquire a process-local lock for append. - open file in append mode with utf-8. - write exactly one entry. - flush/close. - store capture id in sqlite. ## tag normalization org heading tags must look like: ```org :home:errands: ``` normalization: - lowercase - trim whitespace - strip leading `#` - replace spaces with `_` - remove chars not matching `[a-z0-9_@#%]` or choose a stricter set - collapse duplicates - omit empty tags ## heading rules todo: ```org * TODO :tag: ``` note single-line: ```org * note: :tag: ``` note multiline: ```org * note: :tag: ... ``` ## auth use one shared bearer token. do not add oauth, users, sessions, or accounts. ## docker bind port on lan only. do not publish via swag/cloudflare. example: ```yaml services: synq: build: ./server ports: - "8765:8765" environment: PHONE_CAPTURE_TOKEN: "${PHONE_CAPTURE_TOKEN}" PHONE_CAPTURE_ORG_PATH: "/data/synq.org" PHONE_CAPTURE_DB_PATH: "/data/capture.sqlite3" volumes: - /mnt/user/mac/synq/phone-capture:/data ``` ## tests to write first - org formatter: todo with tags. - org formatter: note with no tags. - org formatter: multiline note. - api: valid post appends once. - api: duplicate post appends once. - api: missing token rejected. - api: empty body rejected.