docs: CLAUDE.md — OTM fork context + the first-party-app plan
Build / Build and release (push) Failing after 11s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014LCQmFvKshyAJsE8C3B5ET
This commit is contained in:
2026-10-14 11:23:49 -05:00
co-authored by Claude Fable 5.1
parent d03b120c41
commit d2d8c55d46
+58
View File
@@ -0,0 +1,58 @@
# 13ft — OTM's fork
A self-hosted "read the article" fetcher (Python/Flask + gunicorn, ~200 lines
in `app/` (`portable.py` is the entrypoint)): it refetches a URL as Googlebot and serves the page
without the paywall/ad cruft. Forked 2026-10-14 from
[wasi-master/13ft](https://github.com/wasi-master/13ft) (the original project
was taken down once already — wasm-fyi → wasi-master — which is WHY we hold a
fork, not a dependency on GitHub/Docker Hub).
- **Remotes:** `gitea` = `tonym/13ft` on git.poweredbyotm.com (ours, push here);
`upstream` = GitHub wasi-master/13ft (pull only: `git fetch upstream && git
merge upstream/main`). Token for Gitea lives in `~/Projects/CLAUDE.md`.
- **Running today:** https://13ft.poweredbyotm.com on **anvil**, hand-provisioned
from the STOCK image `wasimaster/13ft:latest` (0.3.4) via
`platform/infra/13ft/setup-13ft-anvil.sh` (platform#91, closed) — a
`/srv/otm/clients/13ft/` compose stack + Caddy site with **basic auth**
(user `tony`; password root-only in `/srv/otm/clients/13ft/secrets/basic-auth.txt`).
Nothing in this repo is deployed yet.
## Where this is going (the reason for the fork)
Make it a proper **OTM first-party app** per
`platform/docs/first-party-app-guide.md` (house rules are CI-enforced by
`platform/test/first-party-house-rules.test.ts`):
1. **Build from this repo** — catalogue entry in
`platform/apps/otm-admin/lib/first-party-apps.ts` (`id: "13ft"`, repo
`tonym/13ft`, image `platform-13ft:latest`, port 5000, `hostname:
13ft.poweredbyotm.com`), then `build_app 13ft` / `deploy_app 13ft` via the
platform MCP. **First: destroy or rename the hand-provisioned `13ft` stack**
(same slug + hostname + container name) or the two will fight over the
Caddy site and the container name.
2. **Sign in with OTM** replaces Caddy basic auth: `needsAuthSecret` →
`OTM_SSO_SECRET`; add `/api/auth/otm-sso` (verify the HS256 ticket for aud
`app:13ft` — port `pickup/src/sso.js`, or in Python: HMAC-SHA256 over
`header.payload`, base64url, check `exp`/`aud`/`jti`, single-use) and a
session cookie; gate every fetch route on it. Grant-only (NOT
`publicAccess`) — a stranger's OTM account must not get a free fetch proxy.
3. **tides** (`needsAnalytics`) — the beacon `<script defer
src="${TIDES_HOST}/t.js" data-site="${TIDES_SITE_ID}">` in the page
template. **Ideas** (`needsSuggestions`) — a lightbulb that files Gitea
issues (see `pickup/src/ideas.js` for the plain-HTTP shape). Version +
`/changelog` + `/api/health` reporting the version + footer "Powered by OTM".
4. Keep the Dockerfile's non-root user (`thirteener`) and gunicorn; add a
`HEALTHCHECK`.
Until step 2 lands, basic auth stays — it is the whole gate.
## Dev
```sh
python3 -m venv .venv && . .venv/bin/activate && pip install -r requirements.txt
cd app && python portable.py # http://127.0.0.1:5000 (the app lives in app/)
docker build -t 13ft-dev . && docker run --rm -p 5000:5000 13ft-dev
```
Upstream README (below in this repo's `README.md`) covers the rest. Keep
upstream's MIT license file.