The Journal

Field notes from the work.

Essays on AI implementation, weekly research briefs from the engine, and operator-grade notes from inside the Tacavar stack.

``` --- ## 6. Admin panel — post management Add this as a second tab in the admin view at `admin.tacavar.com/posts.html`. Full CRUD, with a simple WYSIWYG-ish textarea for body HTML (if you want a real rich editor later we can swap in Tiptap; for now a textarea + preview covers 95% of use cases). Create `/home/tacavar/api/static/posts.html`: ```html ``` The UI pattern matches the lead admin: list on the left, editor on the right. I'll include the full `posts.html` as a separate artifact next if you want — wanted to keep this one focused on the content system itself. --- ## 7. Rollout order 1. Run the SQL migration (`002_posts.sql`) 2. Drop `posts.py` into `app/routes/`, wire it in `main.py`, restart the service 3. Deploy the public `blog.html` at `tacavar.com/blog` (or wherever your current blog lives — this replaces it) 4. Import your existing posts via section 4 5. Add the `publish_brief()` call to R-02 6. First time a brief arrives, approve it from the admin panel → it appears in the journal Briefs land as drafts by default. You never have an agent-written post going live without your sign-off. That's the rule. --- ## 8. What this intentionally avoids - **No comments.** Luxury positioning, and comments become a moderation job. Readers who want to talk email you. - **No email subscriptions yet.** Easy to add later (Buttondown, Substack embed, whatever) once you have ~10 published pieces. - **No RSS feed yet.** 15 lines to add when you want one — let me know. - **No analytics.** Cloudflare gives you server-side analytics for free; no JS tracker needed. Your visitors will thank you. That's the full module. Next up: the industry-aware intake form. # Tacavar — admin.tacavar.com Deployment Two-layer auth for the admin view: - **Layer 1 — Cloudflare Access:** Google SSO on your email. Stops the page from even loading for anyone else. - **Layer 2 — In-page token:** Your existing `API_INTERNAL_TOKEN` gates every API call from the page. Even if Cloudflare were bypassed, the API itself still rejects unauthorized requests. Even if the token leaked, Cloudflare still blocks the page. Defense in depth. --- ## 1. DNS In Cloudflare → DNS for `tacavar.com`: - **Type:** A - **Name:** admin - **IPv4:** your droplet IP - **Proxy status:** Proxied (orange cloud) — this is required for Cloudflare Access --- ## 2. Host the static admin page on the droplet ```bash sudo -iu tacavar mkdir -p /home/tacavar/api/static nano /home/tacavar/api/static/admin.html # paste the Tacavar Admin artifact HTML chmod 644 /home/tacavar/api/static/admin.html ``` --- ## 3. Caddyfile — add the admin.tacavar.com site Add this block to `/etc/caddy/Caddyfile` (alongside your existing `api.tacavar.com` block): ``` admin.tacavar.com { encode zstd gzip root * /home/tacavar/api/static try_files {path} /admin.html file_server header { Strict-Transport-Security "max-age=31536000; includeSubDomains" X-Content-Type-Options "nosniff" X-Frame-Options "DENY" Referrer-Policy "strict-origin-when-cross-origin" # Allow the page to call your API subdomain Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://api.tacavar.com; font-src 'self' data:;" } log { output file /var/log/caddy/admin.tacavar.com.log } } ``` Reload: ```bash sudo systemctl reload caddy ``` Verify the page loads (it will — Cloudflare Access isn't on yet): ```bash curl -I https://admin.tacavar.com # HTTP/2 200 ``` --- ## 4. Cloudflare Access — Google SSO gate You need Cloudflare **Zero Trust** enabled on your account. It's free for up to 50 users, which is plenty. ### 4a. Enable Zero Trust (one time) 1. Log into Cloudflare dashboard 2. Left sidebar → **Zero Trust** 3. Pick a team name (e.g. `tacavar`) — this becomes `tacavar.cloudflareaccess.com` 4. Select the **Free** plan — no credit card required for up to 50 seats ### 4b. Add Google as an identity provider 1. Zero Trust dashboard → **Settings** → **Authentication** 2. Under **Login methods** → **Add new** 3. Pick **Google** (the OAuth option, not Google Workspace) 4. Cloudflare shows you a callback URL like `https://tacavar.cloudflareaccess.com/cdn-cgi/access/callback` 5. In a new tab, go to https://console.cloud.google.com 6. Create a project (or use existing) → **APIs & Services** → **Credentials** → **Create Credentials** → **OAuth client ID** 7. If prompted, configure the OAuth consent screen: - **User type:** External - **App name:** Tacavar Admin - **Support email:** your email - **Scopes:** just `email` and `profile` - **Test users:** add your email 8. Back in Credentials, create **OAuth client ID**: - **Type:** Web application - **Name:** Cloudflare Access - **Authorized redirect URIs:** paste the callback URL from step 4 9. Copy the **Client ID** and **Client Secret** that Google gives you 10. Paste both into the Cloudflare Google IdP form → **Save** 11. Click **Test** — a Google login window should open and come back successful ### 4c. Create the Access application 1. Zero Trust → **Access** → **Applications** → **Add an application** 2. **Type:** Self-hosted 3. Configuration: - **Application name:** Tacavar Admin - **Session duration:** 24 hours - **Application domain:** `admin.tacavar.com` 4. **Identity providers:** check Google (uncheck the default email one-time-PIN if you don't want that fallback) 5. Click **Next** 6. **Policy configuration:** - **Policy name:** Tacavar Owners - **Action:** Allow - **Configure rules:** - Include → **Emails** → add your email address(es) - (Optional) add any team member emails you want to grant access to 7. **Next** → **Next** → **Add application** ### 4d. Verify Open `https://admin.tacavar.com` in an incognito window. You should see the Cloudflare Access login screen before your admin page loads. Log in with Google using your allow-listed email → the admin page appears → paste your internal token → lead queue loads. From a different email (not on the list), Cloudflare Access will refuse. From `curl` without a Cloudflare Access JWT, the request gets a 302 to the login screen. --- ## 5. Tighten the API's CORS to match Since the admin page is now at `admin.tacavar.com`, add that origin to your API's allowed origins. Edit `/home/tacavar/api/.env`: ``` ALLOWED_ORIGINS=https://tacavar.com,https://www.tacavar.com,https://admin.tacavar.com ``` Restart: ```bash sudo systemctl restart tacavar-api ``` --- ## 6. (Optional) Add the service-token pattern for automated access If you ever want to hit the admin API from a script (e.g. a nightly export), Cloudflare Access has **Service Tokens**: 1. Zero Trust → **Access** → **Service Auth** → **Service Tokens** → **Create Service Token** 2. Name it (e.g. `nightly-export`), get back a `CF-Access-Client-Id` and `CF-Access-Client-Secret` 3. In the admin app's policy, add an **Include** rule: **Service Token** → select this token 4. From scripts: ```bash curl -H "CF-Access-Client-Id: " \ -H "CF-Access-Client-Secret: " \ -H "X-Internal-Token: " \ https://api.tacavar.com/v1/leads ``` Skip this unless you actually want programmatic access — the default setup is human-only. --- ## 7. What you now have - `admin.tacavar.com` → Cloudflare Access (Google SSO) → your static admin page - Admin page → `api.tacavar.com` (your FastAPI service) with `X-Internal-Token` - No path exists from the public internet to the admin view without a Google login on an allow-listed address - No path exists from the admin page to the API without the internal token in the browser's session - All Cloudflare Access logins are logged at Zero Trust → **Logs** → **Access** — free audit trail of who viewed what and when That's the whole deploy. About 20 minutes of clicking through Cloudflare and Google, once.