- Python 77.7%
- HTML 10.4%
- JavaScript 4.9%
- CSS 4.7%
- Shell 1.6%
- Other 0.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
db.py og README sa at bare brukere og passord-hasher ligger i databasen, men passnøkler, OAuth-tokens og opplastingstokens ligger der også. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BuSvQFJ3LQqJLEiWXHTueN |
||
| soapbox | ||
| tests | ||
| .env.example | ||
| .gitignore | ||
| ARCHITECTURE.md | ||
| Caddyfile.example | ||
| compose.yaml | ||
| Containerfile | ||
| deploy.sh | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
Soapbox
En liten blogg: statiske sider, mobilvennlig Markdown-editor med bildeopplasting og live forhåndsvisning, innhold versjonert i git. Én hovedbruker («soapbox») pluss valgfrie gjester.
Hvordan det henger sammen
data/
├── content/ git-repo – alt innhold, kan klones/flyttes fritt
│ ├── site.toml tittel, beskrivelse, språk
│ ├── posts/<slug>/index.md + bilder → /<slug>/
│ └── guests/<bruker>/<slug>/index.md → /<bruker>/<slug>/
├── public/ generert statisk side (bygges automatisk ved lagring)
├── soapbox.sqlite brukere, passnøkler og tokens – det eneste som ikke ligger i git
└── secret_key cookie-signering (genereres automatisk)
- Slug lages av tittelen (
Grønt er skjønt→grønt-er-skjønt). Den følger tittelen så lenge innlegget er utkast, og fryses ved publisering så lenker aldri brekker. - Alle interne lenker er relative. Malene regner ut
../ut fra sidens dybde, og absolutte lenker du skriver i Markdown (/annet-innlegg/) skrives om. Derfor fungerer sammepublic/både påblog.domene.no/ogannet-domene.com/blog/uten rebuild. - Bilder ligger i samme mappe som innlegget og refereres som
. Ved opplasting skaleres de ned (maks 2000 px) og EXIF fjernes (ingen GPS-posisjon publiseres). - Hver lagring er en git-commit med brukeren som forfatter. Hvert innlegg har en
«Historikk»-seksjon i editoren med diff per endring; hele loggen ligger under Innstillinger
(eller
git -C data/content log). - Gjester ser og redigerer bare egne innlegg. Hovedbrukeren ser alt og administrerer gjester.
- Med påholden penn: en gjest kan under Profil tillate at hovedbrukeren skriver innlegg i
gjestens navn. Hovedbrukeren får da «Skriv for gjest» på innleggslisten. Innlegget havner under
gjestens
/<bruker>/, frontmatter fårpenholder: "<hovedbruker>", og siden viser «med påholden penn» ved datoen og navnet i signaturen. Git-committen står på den som faktisk skrev. Trekkes samtykket tilbake, kan det ikke opprettes nye slike innlegg, men eksisterende beholder merkingen. - Passnøkler: under Innlogging i admin kan hver bruker legge til passnøkler (fingeravtrykk,
ansikt eller PIN på telefon/PC, eller en sikkerhetsnøkkel) og deretter logge inn uten
brukernavn og passord. Nøklene er bundet til domenet i
SOAPBOX_SITE_URL, så admin må åpnes via den adressen – ikke via IP eller et annet vertsnavn. Passord virker fortsatt ved siden av. - Footeren viser «Dashbord» i stedet for «Logg inn» når du er innlogget. Sidene er statiske,
så
theme/session.jsleser en ikke-hemmelig cookie (soapbox_in) som settes ved innlogging. Egne temaer med egenbase.htmlmå ta med skriptet ogdata-admin-linkpå lenken for å få det. - Tagger settes kommaseparert i editoren.
/tag/<tagg>/lister alle innlegg med taggen (også gjesters),/tag/alle tagger. Tagger får slug på samme måte som titler. - Brukersider:
/brukere/lister hovedbruker øverst og gjester alfabetisk;/brukere/<navn>/viser visningsnavn, beskrivelse (Markdown, settes under Profil), e-post og innlegg. - Signatur: innlegg avsluttes med «– Visningsnavn», evt. med e-post som mailto-lenke. Begge settes under Profil i admin og slås opp ved bygging, så en endring gjelder alle innlegg.
Se ARCHITECTURE.md for moduler, dataflyt og sikkerhetsgrenser.
Skrive med Claude (MCP)
Bloggen har en MCP-server på <SOAPBOX_SITE_URL>/mcp, så Claude kan skrive, endre og publisere
innlegg direkte. Den er av som standard; hovedbrukeren slår den på under Innstillinger →
Tillat Claude (MCP). Når den er av, svarer alle MCP- og OAuth-adresser 404, og avslåing kobler
fra alle apper. Legg den til i claude.ai eller Claude Desktop under Innstillinger → Connectors →
Legg til egen connector med adressen, f.eks. https://naiv.no/bloggen/mcp. Claude Code:
claude mcp add --transport http soapbox https://naiv.no/bloggen/mcp.
- Innlogging skjer med OAuth: Claude registrerer seg selv, du sendes til admin, logger inn som vanlig (passord eller passnøkkel) og godkjenner. Claude får da samme rettigheter som deg – hovedbrukeren kan redigere alt, gjester bare egne innlegg.
- Verktøy:
list_posts,get_post,create_post,update_post,add_image,request_image_uploadogdelete_post. Nye innlegg blir utkast med mindre Claude ber om publisering; slug- og datoreglene er de samme som i editoren, og hver endring blir en git-commit med deg som forfatter. - Bilder Claude har lokalt lastes helst opp via
request_image_upload: verktøyet gir en engangs-URL (<SOAPBOX_SITE_URL>/api/upload/<token>) som Claude laster opp til medcurl -T <fil> <url>, så bildedataene ikke går gjennom MCP-kallet. Tokenet er eneste autentisering, gjelder ett innlegg, kan brukes én gang og utløper etterSOAPBOX_UPLOAD_TOKEN_TTLsekunder.?alt=overstyrer alt-teksten. Alternativt kanadd_imagehente fra en https-URL (serveren henter selv, og nekter interne adresser som localhost, private nett og sky-metadata) eller ta imot base64. Bildene skaleres ned og EXIF fjernes som ved vanlig opplasting. - Illustrasjoner Claude lager selv kan sendes som SVG-tekst til
add_image(svg, valgfriwidth). Serveren rasteriserer med resvg og lagrer bare PNG-en; SVG-en serveres aldri. SVG med skript,foreignObject,on*-attributter, DTD/entiteter eller eksterne referanser avvises. Tekst bruker systemfontene (DejaVu i containeren). - Med påholden penn:
create_postmedon_behalf: "<gjest>"virker for hovedbrukeren når gjesten har tillatt det under Profil, akkurat som «Skriv for gjest» i admin. - Koble fra under Innlogging i admin. Bytter du passord, kobles alle apper fra.
- Bare SHA-256 av tokens lagres i
soapbox.sqlite. Access-tokens varer en time, refresh-tokens 30 dager og roteres ved bruk. - Under sub-path finner Claude OAuth-oppsettet under
/bloggen/selv, men eldre klienter leter på domeneroten (/.well-known/oauth-authorization-server/bloggeno.l.). Rut disse stiene til appen, seCaddyfile.example.
Kjøre lokalt
uv sync
SOAPBOX_DATA_DIR=./data uv run soapbox create-user olemd --role owner
SOAPBOX_DATA_DIR=./data uv run soapbox serve --debug
# → http://localhost:8080/admin/
Tester: uv run pytest. Lint: uv run ruff check . && uv run ruff format .
Konfigurasjon (miljøvariabler)
Se .env.example for en kommentert mal. Alt har standardverdier; kun SOAPBOX_SITE_URL og
hovedbrukeren bør settes.
| Variabel | Standard | Beskrivelse |
|---|---|---|
SOAPBOX_DATA_DIR |
./data (/data i container) |
Alt persistent |
SOAPBOX_BASE_PATH |
stien i SOAPBOX_SITE_URL |
Overstyring, kun hvis proxyen stripper prefikset |
SOAPBOX_SITE_URL |
http://localhost:8080 |
Absolutt URL til rot inkl. sub-path; brukes i Open Graph, feed, sitemap og til å utlede base-path |
SOAPBOX_THEME |
green |
Mappe under soapbox/themes/ |
SOAPBOX_MAX_IMAGE_PX |
2000 |
Lengste side på opplastede bilder |
SOAPBOX_UPLOAD_TOKEN_TTL |
600 |
Sekunder en opplastings-URL fra request_image_upload er gyldig |
SOAPBOX_UPLOAD_MAX_BYTES |
20971520 |
Maks størrelse på fil lastet opp til en slik URL |
SOAPBOX_SECRET_KEY |
autogenerert | Overstyr hvis du vil ha nøkkelen utenfor volumet |
SOAPBOX_OWNER_USER / _PASSWORD |
– | Oppretter hovedbruker ved første start |
Container og Caddy
cp .env.example .env # sett SOAPBOX_SITE_URL og hovedbruker
./deploy.sh # bygger localhost/soapbox:latest og starter containeren med .env
./deploy.sh --build-only # bare bygg (med build-metadata)
./deploy.sh --run-only # bare start/erstatt containeren, lytter på SOAPBOX_BIND:SOAPBOX_PORT
# eller: podman compose up -d (se ./deploy.sh --help for alle valg)
Ny versjon: git pull && ./deploy.sh. Data ligger i podman-volumet soapbox-data
(podman volume inspect soapbox-data viser stien).
deploy.sh tar automatisk backup av volumet før hver oppstart til SOAPBOX_BACKUP_DIR
(standard ./backups/soapbox-data-<tidsstempel>.tar.gz) og beholder de SOAPBOX_BACKUP_KEEP
nyeste (standard 5). Gjenopprett med podman volume import soapbox-data <fil>.
Caddy er ren reverse proxy, se Caddyfile.example. Appen lytter som standard på alle adresser
(IPv4 og IPv6), så Caddy kan stå på en annen maskin og nå den over Tailscale/MagicDNS. Sett
SOAPBOX_BIND=127.0.0.1 for å begrense når Caddy kjører lokalt. Ved sub-path skal Caddy ikke strippe
prefikset – appen monteres selv under stien fra SOAPBOX_SITE_URL.
Flytte bloggen (nytt domene og/eller sub-path)
Ingen innhold trenger endring – alle lenker er relative. Velg én av to måter:
A. Kopier hele data/-volumet. Tar med innhold, brukere og cookie-nøkkel. Ingenting må
opprettes på nytt.
podman volume export soapbox-data | ssh ny-server 'podman volume import soapbox-data -'
B. Klon bare innholdsrepoet. Brukere ligger i sqlite, ikke i git, så de må opprettes på nytt
(hovedbruker via SOAPBOX_OWNER_USER/_PASSWORD eller soapbox create-user, gjester i admin med
samme brukernavn som før – brukernavnet er URL-prefikset /gjest/).
git clone gammel-server:/sti/til/data/content ./data/content
Deretter, uansett metode: sett SOAPBOX_SITE_URL til den nye adressen (sub-path utledes
derfra) og start. Siden bygges på nytt ved oppstart.
Tema
Aktivt tema velges under Innstillinger i admin og lagres i site.toml (følger med i git).
SOAPBOX_THEME er bare standardverdien før noe er valgt.
Lag et eget tema uten å bygge containeren på nytt: legg det i innholdsrepoet, så følger det med i git, backup og ved flytting.
mkdir -p data/content/themes/skog/static
cp soapbox/themes/green/static/style.css data/content/themes/skog/static/ # rediger fargene
Et tema kan være delvis: bare filene som finnes overstyrer det innebygde green-temaet.
Vil du endre malene, legg templates/base.html, index.html eller post.html i temamappen.
Malene får site, posts/post, html, source_url og rel(sti) (relativ lenke fra
gjeldende side), samt filtrene date, excerpt(n) og tagslug. Bruk asset('theme/fil.css') for temafiler:
den gir en relativ URL med innholdshash (?v=…) for cache-busting. static/ havner under /theme/.
Lisens
AGPL-3.0-or-later, se LICENSE. Bloggen lenker til kildekoden i footeren, slik lisensen
krever for nettverkstjenester. Endrer du koden og hoster den, må lenken peke til din versjon
(SOURCE_URL i soapbox/build.py).
Veien videre (ikke i denne omgangen)
- Poste lenker til fediverset/Bluesky og vise svar som kommentarer.
- Push av innholdsrepoet til en ekstern git-remote.