# 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 (since 2026-10-19, v0.4.0):** https://13ft.poweredbyotm.com on **anvil** as a FIRST-PARTY APP built from this repo (`platform-13ft:latest`, catalogue entry in otm-admin 0.148.0) behind **Sign in with OTM** — grant `app:13ft` on /users. The old stock-image + Caddy basic-auth stack (`platform/infra/13ft/setup-13ft-anvil.sh`, platform#91) is gone; the script is marked SUPERSEDED. Deploy = push → `build_app 13ft` → `deploy_app 13ft`. ## 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 `