24 KiB
Content master handover: Molodetz
Audience: the coworker agent that takes over content for production Molodetz. This is not an engineering or DPP-template brief.
| Fact | Value |
|---|---|
| Production host | https://molodetz.nl |
| Staging host | https://staging.app.molodetz.nl (proxies 127.0.0.1:19847) |
| Test host | https://molodetz-test.app.molodetz.nl (proxies 127.0.0.1:19849) |
| Local/dev | http://127.0.0.1:8088 (make dev) |
| Git remote | https://retoor.molodetz.nl/retoor/ad.git (private Gitea) |
| Local clone | /workspace/retoor/molodetz-app |
| Package | molodetz |
| Language on site | English only |
| Owner / sole public author today | retoor |
| Old brand | Absurd Development (git repo name ad; public product name Molodetz) |
DEVELOPMENT_FLOW.md in this repo is the multi-agent engineering lesson
log that may feed the DPP template. It is not the content bible.
Content work stays here and on the live site. Do not fold Molodetz-specific
voice, memes, or intro text into /workspace/dpp/dptemplate.md.
Production admin password and SECRET_KEY live with the operator / secrets
store (compose PROD_* env). Never put production passwords, API keys,
or .env values in this file, in posts, in captions, or in git.
0. Repository and code changes
Private Gitea repository:
https://retoor.molodetz.nl/retoor/ad.git
- Clone, commit, and push code (gallery wiring, captions in
gallery.py, generator scripts if promoted into the repo) against that remote on branchmainunless ordered otherwise. - Auth for git: Gitea user credentials from the operator secrets store
(same class of secret as
~/.config/gitea/retoor.molodetz.nl.envon the build box). Use a one-shot credential helper orGIT_ASKPASS; never embed the password in the remote URL, in.git/config, in this file, or in chat logs. - Content-only work that only needs admin UI/API does not require a push.
Changing published gallery files without updating
GALLERYin git will drift; prefer commit+push for media registry changes. - Markdown files (including this one) need explicit user confirmation before commit.
0.1 Machine-readable docs for agents
Before calling write APIs, load the live docs so samples stay in sync with
the registry (molodetz/docs_api/*, built by molodetz/docs_export.py):
| Doc | Production URL | Notes |
|---|---|---|
| Docs home | https://molodetz.nl/docs |
Sidebar + prose |
| API groups (HTML) | https://molodetz.nl/docs/api/content, .../join, .../account, .../admin |
Admin group requires an admin session |
| Full download (markdown) | https://molodetz.nl/docs/download.md |
Auto-generated: every endpoint with method, path, auth, fields, curl, request JSON, response sample |
| Full download (HTML) | https://molodetz.nl/docs/download.html |
Same content, HTML |
| OpenAPI | https://molodetz.nl/swagger |
Framework OpenAPI UI (/openapi.json) |
| Auth prose | https://molodetz.nl/docs/api |
How sessions and API keys work |
Replace the host with staging or test URLs when working on those environments. Downloads omit the Admin group unless the request is authenticated as admin.
Auth (Account group)
No JWT. No OAuth. Every HTML route also returns JSON when called with
Accept: application/json.
- Session cookie (64 hex):
POST /auth/loginwithusername,password, optionalremember. Cookie lasts 7 days (30 with remember). Sample success:{"ok": true, "redirect": "/admin", "data": null}. X-API-KEY: after login,GET /profile/api-key(member+). Sample shape{"api_key": "..."}. Destructive renew:POST /profile/api-key/regenerate.- Also accepted by the app (see docs prose):
Authorization: BearerandBasicwith the same credential resolution as the API key path.
Log out: POST /auth/logout. Accept terms if gated:
POST /terms/accept (member).
Public read (Content group)
Use these to verify what the site shows after you publish. All auth=public.
| Method | Path | Purpose | Response sample (registry) |
|---|---|---|---|
| GET | / |
Home + featured flyer counts | {"posts": [], "flyer_count": 4, "meme_count": 22} |
| GET | /roll |
Roll feed; query before cursor |
{"title": "Roll", "topic": "roll", "posts": [], "next_cursor": null} |
| GET | /standard |
Standard feed | {"title": "Standard", "topic": "standard", "posts": []} |
| GET | /posts/{slug} |
One post | {"post": {"title": "...", "topic": "roll"}, "author": {"username": "retoor"}} |
| GET | /flyers |
Flyer gallery | {"kind": "flyer", "title": "Flyers", "items": []} |
| GET | /memes |
Meme gallery | {"kind": "meme", "title": "Memes", "items": []} |
| GET | /people |
People list | {"people": [{"username": "retoor", "post_count": 5}]} |
| GET | /people/{username} |
Profile + posts | {"person": {"username": "retoor"}, "posts": []} |
| GET | /health |
Liveness | {"status": "ok", "version": "..."} |
There is no public or admin HTTP API to upload a new meme/flyer file.
Galleries are files under molodetz/static/media/ plus rows in
molodetz/gallery.py synced on boot. Content master still uses Content
GET to confirm captions and order after deploy.
Example:
curl -H 'Accept: application/json' https://molodetz.nl/roll
curl -H 'Accept: application/json' https://molodetz.nl/flyers
Join group (visitor intake; admin triage separately)
| Method | Path | Auth | Body / notes |
|---|---|---|---|
| GET | /join |
public | Form; sample {"submitted": false} |
| POST | /join |
public | Required name, contact; optional repo_url, message. Sample: {"ok": true, "redirect": "/join?ok=1", "data": {"uid": "0190..."}} |
Join rows are never public. Content master does not invent join stories. Admins list and update them under Admin (below).
Admin group (content master write path)
Requires admin role (retoor). Prefer session cookie after UI login, or
admin API key via X-API-KEY. Always send Accept: application/json
for machine clients.
Posts
| Method | Path | Purpose | Body fields (registry) |
|---|---|---|---|
| GET | /admin/posts |
List | |
| GET | /admin/posts/new |
New form | |
| POST | /admin/posts |
Create | required title, body; topic example roll (also standard); status example draft (published); optional is_placeholder |
| GET | /admin/posts/{uid}/edit |
Edit form | path uid |
| POST | /admin/posts/{uid} |
Save | path uid + same fields as create |
| POST | /admin/posts/{uid}/publish |
Toggle publish | path uid |
| POST | /admin/posts/{uid}/delete |
Soft delete | path uid |
Create sample response:
{"ok": true, "redirect": "/admin/posts", "data": {"uid": "..."}}.
Example create (password never stored here; use operator secrets):
curl -X POST https://molodetz.nl/admin/posts \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-API-KEY: $MOLODETZ_API_KEY" \
-d '{"title":"A title","body":"Text in **markdown**.","topic":"roll","status":"draft"}'
Home intro (settings)
| Method | Path | Purpose |
|---|---|---|
| GET | /admin/settings |
Read settings including site_intro |
| POST | /admin/settings |
Save settings (form/JSON; include site_intro markdown) |
Join triage
| Method | Path | Purpose | Body |
|---|---|---|---|
| GET | /admin/joins |
List requests | |
| POST | /admin/joins/{uid}/status |
Set status | status in open, contacted, accepted, declined |
| POST | /admin/joins/{uid}/check |
Fetch repo link title via guarded outbound client |
Trash (restore mistaken deletes)
| Method | Path | Body |
|---|---|---|
| GET | /admin/trash |
|
| POST | /admin/trash/restore |
required stamp |
| POST | /admin/trash/purge |
required stamp (destructive) |
Other admin endpoints (services, backups, users, audit, stats) are
operations, not day-to-day content. See /docs/api/admin or
download.md while logged in as admin for the full list. Do not schedule
backups or purge trash unless ordered.
Source of truth
Endpoint tables above are copied from molodetz/docs_api/content.py,
join.py, account.py, and admin.py as of this handover. If the
registry changes, trust live https://molodetz.nl/docs/download.md
over this file and update the handover when you next touch it.
1. What the site is built for
Molodetz is a community blog roll: calm elite serif UI, slightly provocative content, markdown posts in SQLite, two galleries (flyers and memes), a people list, and a real join form.
Purpose, as stated in the home intro (site_intro, seeded from
molodetz/seed/intro.md) and in SELECTION.md:
- Hostile, data-backed elite intro: floor = current skill; the site does not flatter mediocrity.
- Help people reach elite level, especially by learning to use AI as a craft tool, not as a substitute for judgement.
- Divide honestly: classic hand-coders who refuse AI, vibe "coders" who refuse to learn to code, and the elite twice path (classic craft + AI).
- Offer a path: take the profession seriously; ship; read every line the machine writes.
- Side product context in the intro: a development environment for the elite (vim/tmux-class toolkit plus AI skill). Do not invent product claims beyond what retoor publishes.
SELECTION.md one-liner: "Calm elite serif community blog roll for
retoor. English. Posts, flyers, memes, people, join intent."
2. Intentions
- Fight mediocrity. A pass is the floor, not an ambition (seed roll post and retired Dutch assets said the same thing; English site keeps it).
- Divide and offer a path. Look at the person next to you: replaced or replacing.
- Promote elite programmers who embraced AI and kept craft: "elite twice" (know the code, use the machine, read every line, ship).
- Roast classic programmers who refuse AI on principle (vim/tmux purity, "I don't need autocomplete", compilers-made-us-lazy recycling).
- Roast vibe coders who refuse to learn to code / do not take the profession seriously (prompt-and-pray, tab-tab-accept-all, README with badges and zero tests, course before craft).
- Facts over feelings. Pride backed by data is allowed; apology spirals are not the brand.
3. Tone
| Do | Do not |
|---|---|
| Harsh, rude, elitist when earned | Soft HR copy, engagement bait |
| Facts, never invented lies | Fake testimonials, fake members |
| Based, conservative craft ethos | Hype, hustle-culture emptiness |
| Pro-AI and pro classic elite; especially the combo | Pro-AI that excuses ignorance of code |
| Proud and specific | God-complex apology spiral |
| English | Dutch on the public site |
| Short punchy posts and one-point flyers | Em-dashes, emoji decoration, slurs |
Banned characters in shipped prose and captions: em dash (U+2014) and en dash (U+2013). Use commas, periods, semicolons, or hyphens.
4. What NOT to do
- Invent members, join success stories, or co-authors. People today is effectively retoor only until real people join and are approved.
- Selfie / photo of retoor as gallery art.
- DevPlace, Nigel, or other named private individuals in memes or posts.
- JWT, OAuth-as-flex, framework catalogue flex, payment/ad SDK vibes in content (engineering also forbids JWT; content should not romanticize it).
- Dutch copy on public pages, captions, or new assets. Old Dutch media
files may remain on disk under
RETIRED_GALLERYbut must stay unpublished. - Em-dashes in UI copy, posts, captions, alt text, or image text.
- Commit secrets,
.env, production passwords, API keys, or dump them into CONTENT docs. - Weaken factual claims in the intro without retoor's order. Do not "tone down" the hostile frame into a friendly corporate blog unless ordered.
- Fold Molodetz voice into the DPP template or treat
DEVELOPMENT_FLOW.mdas a place to store meme recipes.
5. Content surfaces
| Surface | URL | What it is | Who edits |
|---|---|---|---|
| Home intro | / |
site_intro setting (markdown). Seed: molodetz/seed/intro.md. Hostile elite manifesto. |
Admin Settings |
| Roll | /roll |
Short notes, newest first. Topic roll. Seed placeholders exist. |
Admin Posts |
| Standard | /standard |
Standards / taste posts. Topic standard. Seed: "Thirty-one choices. You have zero." (DPP-ish stack choices, no JWT, no JS framework). |
Admin Posts |
| Flyers | /flyers |
Portrait gallery. Kind flyer. Featured flyer on home = first flyer by gallery position (flyer-elite-twice.jpg). |
Files + molodetz/gallery.py sync |
| Memes | /memes |
Square gallery. Kind meme. Classic / vibe / elite sets + a few older English leftovers. |
Files + gallery sync |
| People | /people, /people/retoor |
Authors with posts. | Real accounts only; do not invent |
| Join | /join |
"I'm in" form; stored for admins, never public. | Visitors submit; admin triage at /admin/joins |
| Docs | /docs |
Operator/API docs. Not a content marketing channel. Contributing/component docs were removed on purpose. | Engineering |
Seed posts (English; from molodetz/content.py)
| Topic | Title | Role |
|---|---|---|
| standard | Thirty-one choices. You have zero. | Stack / taste standard |
| roll | A pass is not an ambition. | Floor / mediocrity |
| roll | Finished is a feature. | Ship |
| roll | The assistant is not a senior. | AI without judgement |
| roll | No course. | Anti weekend-ebook path |
Roll seeds are created as placeholders (is_placeholder); bodies may
carry a carried-over note. Prefer replacing placeholders with real retoor
stories over deleting the point.
Live gallery (published English set)
Flyers (1080x1350), order matters (home featured = first):
flyer-elite-twice.jpg- Elite twice.flyer-taste.jpg- Taste does not autocomplete.flyer-demo.jpg- A demo is not a product.flyer-hands.jpg- Your hands are not the craft.
Memes (1080x1080): six classic, six vibe, six elite (meme-classic-*,
meme-vibe-*, meme-elite-*), plus older English leftovers
meme-compiles.jpg, meme-senior.jpg, meme-trophy.jpg,
meme-todo.jpg (legacy 1280x720 era; keep or retire only on order).
Retired on disk, unpublished (RETIRED_GALLERY in gallery.py): old
Dutch flyers/memes/posters/takes (flyer-keuzes.jpg, meme-vloer.jpg,
poster-*, take-*, story.jpg, ...). Soft-deleted in DB. Do not
re-publish.
Captions/alt live in GALLERY tuples and sync into media_items on boot.
English only. Captions must not start with SQL keywords that trip
sqlglot string lint (SELECT, INSERT, ...).
6. How to publish
Prefer the admin HTML UI for interactive work. Prefer the Admin API (section 0.1) for scripts and agent automation. Both share the same handlers.
Login
- Open
https://molodetz.nl/auth/login(or local/staging equivalent). - Username:
retoor(production admin username from env bootstrap). - Password: production admin password is with the operator / secrets
store, never in the repo. Staging/test may use compose defaults
documented in
README.md; production usesPROD_ADMIN_PASSWORD. - Session cookie, 7 days (30 with remember). No JWT.
Edit home intro
/admin/settings- Field
site_intro(markdown textarea). - Save. Home re-renders from settings (landing cache TTL applies).
Create / edit / publish a post
/admin/postsor/admin/posts/new- Title, markdown body, topic
rollorstandard, status draft or published. - Publish/unpublish from the posts list.
- Soft delete goes to trash; restore from
/admin/trashif needed.
Join requests
/admin/joinsfor open requests.- Status and optional repo-link check (outbound stealth client).
- Do not invent acceptances in public content.
Galleries (flyers / memes)
- Add JPEG under
molodetz/static/media/with an English filename. - Register in
GALLERYinmolodetz/gallery.pywith kind + English caption (alt text). - Put new flyers in the desired order; index 0 is the home featured flyer.
- Deploy / restart so
sync_gallery()runs (bootstrap). Dedupe uses perceptual hash distance<= 2; near-duplicate layouts are skipped. - Soft-retire old files via
RETIRED_GALLERY+retire_media; keep bytes on disk unless ordered otherwise. - Visual-check every new image (open the PNG/JPG; spelling, crop,
legibility). Build a contact sheet when shipping a set
(
out/memes-sheet.jpgpractice).
Engineering owns compose, molohttp, and data trees
(./data/production is holy). Content master does not refresh staging
from production or touch molohttp unless explicitly ordered.
7. Meme / flyer production recipe
Generator scratch (not committed):
/workspace/tmp/molodetz-app-20261005/memes/
(lib.py, parts.py, classic.py, vibe.py, elite.py, flyers.py,
gen.py). Prefer regenerating with Pillow so all text is drawn, never
baked into a copyrighted meme photo.
Dimensions and output
| Kind | Size | Format |
|---|---|---|
| Meme | 1080 x 1080 | JPEG, quality ~93, subsampling 0 (lib.save) |
| Flyer | 1080 x 1350 | same |
Fonts (from lib.py FONT_FILES)
Base dirs:
- Google:
/usr/share/fonts/truetype/sand-box/google/ - Custom:
/usr/share/fonts/truetype/sand-box/custom/ - DejaVu:
/usr/share/fonts/truetype/dejavu/
| Key | File | Typical use |
|---|---|---|
anton |
Anton/Anton-Regular.ttf | Meme titles, impact headers |
archivo |
Archivo Black/ArchivoBlack-Regular.ttf | Dense meme body |
mono / monob / monosb |
IBM Plex Mono Regular/Bold/SemiBold | Terminals, keys |
space |
Space Mono/SpaceMono-Bold.ttf | wordmark molodetz |
serif / serifi |
DM Serif Display Regular/Italic | Flyer headlines |
caslon / caslonb / casloni |
Libre Caslon Text | Flyer body / italics |
sans / sanssb / sansb / sansblack |
Source Sans Pro | UI labels, flyer header |
sym / symb |
DejaVu Sans | Glyphs (e.g. crown) |
marker |
Permanent Marker | Occasional accent |
Text fitting: block() / fit() must succeed; never ship cropped
letters. Uppercase via upper=True when the layout calls for it.
Wordmark
- Literal text:
molodetz(lowercase). - Font: Space Mono Bold, default size 24, pad 30.
- Default corner: bottom-right (
corner="br"); flyers draw bottom-right with ink; some dark memes use a dark plate behind the mark or bottom-left when a layout conflicts. - Alpha ~170-230 depending on background contrast.
Flyer palette and layout (flyers.py)
- Paper cream
PAPER = (243, 238, 227) - Ink
(28, 26, 24), muted(110, 102, 92), accent red(178, 48, 36) - Margin
M = 96 - Header: spaced
MOLODETZ(Source Sans Bold 26) +No. N of 4(Caslon italic) - Motif, then DM Serif headline, short red rule, Caslon body
- Footer:
ONE POINT PER SHEET+ wordmark - One point per sheet. Calm but cutting. English.
Current four points: elite twice; taste does not autocomplete; a demo is not a product; your hands are not the craft.
Meme palettes and targets
Roughly one third each:
- Classic (refuse AI) - dark terminals, cream starter packs, charts
where keystrokes rise and features stay flat, certificates of manual
labour, multi-generation refusal. Colors: near-black gradients, cold
greys, warning red
(255, 95, 86), occasional yellow band. - Vibe (refuse craft) - Win95-style prompt-and-pray, pink/purple galaxy "vibe debugging", starter packs (TAB, sk-live-, localhost), weekend-to-course arcs, Drake-style NAH/YEP priorities.
- Elite (embrace AI + craft) - gold
GOLD = (255, 196, 60), dark navy/black panels, crown glyph, build logs that review every line, choose-your-fighter with elite column highlighted, curves that keep climbing, achievement unlocked.
Classic formats allowed as layouts only (two-panel, galaxy brain, Drake approve/reject, starter pack, fake terminal, fake error, fake chart, certificate). Draw backgrounds programmatically or with non-copyrighted generation. No stolen meme template photos.
Dev-culture references encouraged: vim/tmux, Stack Overflow, works on my machine, README-driven, 10x, git blame, compiler errors, prompt-and-pray, tab-tab-tab accept-all, punch cards, etc.
Content bans on assets
No slurs. No real named individuals. No em-dashes in image text. No DevPlace/Nigel. No selfie. English filenames and English captions.
Wiring and QA
- Generate into scratch
out/, visual-read every file, fix spelling. - Copy into
molodetz/static/media/. - Update
GALLERYcaptions; retire Dutch leftovers inRETIRED_GALLERY. - Contact sheet of the full new set (filenames under each tile).
- Restart / sync; confirm
/flyers,/memes, home featured flyer. - Keep pairwise phash distance clearly above 2 so sync does not drop "duplicates".
8. Consistency checklist (every new asset or post)
- English only; no Dutch UI copy.
- No em-dash or en-dash characters.
- Tone matches section 3 (harsh when earned, factual, pro elite+AI).
- Targets the right enemy or the elite path; does not punch down with slurs or real names.
- Post: correct topic (
rollvsstandard); markdown renders; no secret material. - Meme 1080x1080 or flyer 1080x1350; wordmark present; text fully visible (no clip).
- Caption/alt English, honest, not SQL-keyword-leading.
GALLERYupdated or post published via admin; retired files not re-listed.- Visually verified (Read/open the image or preview the post).
- No commit of
.env, passwords, or private join PII.
9. Success criteria for the content master
- Home intro stays hostile, data-backed, and accurately retoor's voice unless retoor orders a rewrite.
- Roll accumulates real finishing/AI/craft stories; placeholders shrink.
- Standard stays taste-and-standards, not tutorial spam.
- Galleries keep the three-way thesis (classic roast / vibe roast / elite twice) with consistent visual system and English captions.
- Featured flyer remains the thesis sheet (elite twice) unless retoor reorders deliberately.
- Join queue is handled as private admin work; public site never fakes community size.
- No Dutch, no em-dashes, no secrets, no invented people in anything shipped.
- New assets are reproducible from the Pillow recipe (or an equal programmatic successor), not one-off unreproducible binaries without text sources.
- Content changes do not require inventing engineering; when gallery code or settings must change, coordinate with engineering and keep tests green on their side.
- Coworker can ship a new meme set or post week using only this document, admin login (secrets from operator), and the generator path above.
Quick reference paths
| Item | Path / URL |
|---|---|
| Git remote | https://retoor.molodetz.nl/retoor/ad.git |
| App repo (local) | /workspace/retoor/molodetz-app |
| Production | https://molodetz.nl |
| Staging | https://staging.app.molodetz.nl |
| Test | https://molodetz-test.app.molodetz.nl |
| Docs download | https://molodetz.nl/docs/download.md |
| OpenAPI | https://molodetz.nl/swagger |
| API registry (code) | molodetz/docs_api/ |
| Download builder | molodetz/docs_export.py |
| Intro seed | molodetz/seed/intro.md |
| Gallery registry | molodetz/gallery.py |
| Media files | molodetz/static/media/ |
| Seed posts | molodetz/content.py SEED_POSTS |
| Feature selection | SELECTION.md |
| Compliance vs DPP | COMPLIANCE.md (engineering; not tone) |
| Engineering lessons (not content) | DEVELOPMENT_FLOW.md |
| Meme generator scratch | /workspace/tmp/molodetz-app-20261005/memes/ |
| Deploy notes | README.md, molohttp_deploy.md |