# msge.no (full context) This file provides machine-readable context for LLMs and crawlers. msge.no is the personal website of Markus M-G. It is a slim overview page (https://msge.no/) backed by several focused detail pages. ## Site purpose A personal hub with: - a short profile and curated links - a combined "more" page for reading, music, train travel, photos and notes - three sections — trains, books and film — each a hub, an archive with per-axis drilldown pages, and one or more display pages - standalone pages for the technology graph, an AI-disclosure note, a Mastodon engagement dashboard, and a real-time activity feed ## Pages - Overview: https://msge.no/ — hero, curated links, and a glanceable "Akkurat no" row (now reading, now playing, the train he is on right now or the next one, and the last thing he watched), each linking to the section that owns it. - More (details): https://msge.no/meir/ — reading (Les), music (Lyttar), train travel (Tog), photos (Bilete) and notes (Tankehav), plus writing/working/about. - Bok (section hub): https://msge.no/bok/ — what Markus is reading right now, the running totals, and the way into the shelf and the archive. - Bookcase: https://msge.no/bok/hylla/ — every book Markus has read, drawn as coloured book spines (thickness ∝ page count, colour = genre); all-time view with a per-year toggle and shareable per-book deep links (?b=; the older #b= form still resolves). Was https://msge.no/bokhylla/, which 301s here. - Book archive: https://msge.no/bok/arkiv/ — the shelf aggregated four ways, as tabs: by genre, by calendar month, by author and by language. A page per key at /bok/arkiv/{sjanger,manad,forfattar,sprak}/. THE COVERAGE BLOCK IS PART OF THE DATA: the shelf begins at a StoryGraph export, so everything read before it is simply absent, BookWyrm's own count is higher than the one served here, and the running year and running month are drawn hatched and held out of every ranking and average. - Technology graph: https://msge.no/stacken/ — an interactive D3 force graph of which languages/frameworks/tools co-occur across Markus's GitHub repos. - AI disclosure: https://msge.no/ki/ — a short bilingual (Nynorsk + English) first-person note that these sites are built primarily with agentic AI coding, human-directed and human-accountable. This is the page an LLM crawler should be pointed at. - Engagement dashboard: https://msge.no/blogg/engasjement/ — favourites, boosts and replies for @markus@skvip.lol, with date filtering and top-post/month/year views. This was /skvip until #236; the old address 301s here and the query string survives. - Tog: https://msge.no/tog/ — the hub of the train section: the newest togselfie as the hero, the next departure (or the leg under way) as one card, and Viaduct's own map of every trip. Four ways in from there: Toget no, Tavla, Selfiar and Arkivet. - Toget no: https://msge.no/tog/no — the leg Markus is on right now, stop by stop on the real track, with a progress bar, a route map and realtime times from Entur where it has coverage (Norway and cross-border), plus the posts he wrote aboard. Progress on legs Entur cannot see is computed from the timetable and labelled as such, and it carries no punctuality line at all, since a timetable cannot know about delays. - Tavla: https://msge.no/tog/tavla — the next train Markus is taking, drawn as a station departure board, and the one page on the site built to be embedded in an iframe by other people. It always shows a next departure however far out it is (a real one is often two months away), so the board is never empty, and it never renders a departure that has already gone. Track ("spor", from Entur's quay.publicCode), punctuality, cancellations and disruption messages exist ONLY for Norwegian departures within two hours of leaving, because that is the only window the Entur poller is gated open for; outside it the track column reads "—" and the status column reads "RUTETID" rather than claiming a train is on time it has not measured. Framing is declared rather than merely unforbidden (Content-Security-Policy: frame-ancestors *), and three query parameters shape the embed: ?tema= (mork · lys), ?rader= (1–10) and ?kompakt=1. An unknown value falls back to the default, because an embed URL is a link somebody else wrote. - Arkivet: https://msge.no/tog/arkiv — everything travelled, seen four ways as tabs. Land: kilometres by train in each country, derived offline by routing every station pair over the rail network and intersecting it with borders, one page per country at /tog/arkiv/land/. Månader: a year × month heatmap and the busiest months ranked, one page per month at /tog/arkiv/manad/; the running month is drawn hatched and held out of every ranking and average, since a month three days in is not comparable with a finished one. År: the archive by calendar year — a year in review, one page per year at /tog/arkiv/aar/. Each year leads with its SHARE of all travel ever, and that share exists only for quantities that actually sum to the total: kilometres, hours, legs, night trains, and journeys counted on the year they started. Countries, stations and operators recur year after year, so their shares would sum far past 100 % — they come as "n of m" counts and never as a percentage. And unlike the running month, the running year is marked "ikkje omme" but still ranked and averaged: a year in review that hides the current year is a look back with no present in it. Reiser: the named multi-train journeys, one page each at /tog/arkiv/reise/, each leading with a route sketch drawn from real station coordinates or with no sketch at all. - Togselfie gallery: https://msge.no/tog/selfie/ — every #togselfie post from @markus@skvip.lol as a chronological grid, newest first, read row for row. Two tabs — the photographs and a locations map (?view=map) — with the rolling-stock tally (“Samling”) and the face-lock reel (?view=reel) as their own buttons beside them. The alt text (“toginfo”) and the status text expand in place on each card via an ⓘ button; the old ?view=kort spelling still resolves and lands on the photographs. Per-image deep links (?b=) and per-facet filters (?m=:). - Togselfie reel: https://msge.no/tog/selfie?view=reel — autoplaying full-screen reel of the face-bearing togselfies, face pinned via precomputed landmarks. Was https://msge.no/tog/selfie/alle, which 301s here. - Popularity ranking: https://msge.no/poppis/ — human page views for the open sites and msge.no pages over selectable time windows. - Film (section hub): https://msge.no/film/ — the last thing Markus watched, the running totals, and the way into the wall, the archive and Rulleteksten. - Rulleteksten: https://msge.no/film/rulleteksten/ — every film and TV series marked as watched in the ActivityPub archive, and the hidden web of shared cast members between them. The recurring names, the ties, the solitary titles and the single cut-vertex "pin" are all derived at runtime from the live data (never asserted). Cast names in non-Latin scripts are transliterated via a maintained alias map; /film/data.json exposes the derived graph (permissive CORS) and /film/ukjende lists names still missing from the map — both keep answering at those addresses after the page moved, and are also aliased under /film/rulleteksten/. - Plakatveggen: https://msge.no/film/plakaten/ — every film and TV series Markus has watched, drawn as a wall of posters, newest first. Filterable by watch-year and by genre, with a stats strip (totals, film/series split, cinema count, span). Derived from the same /watched rows as Rulleteksten, by the same poller — one fetch, two views. Genres are normalised through a shared alias map and bucketed into a small palette; "seen at the cinema" is read out of Markus's own free-text notes, so it is an estimate and the note itself is shown alongside. Posters are hotlinked from NeoDB. Per-title deep links at /film/plakaten?b=. Was https://msge.no/plakaten/, which 301s here. - Film archive: https://msge.no/film/arkiv/ — the same rows aggregated four ways, as tabs: by genre, by calendar month, by director and by decade of release. A page per key at /film/arkiv/{sjanger,manad,regissor,tiar}/. THE COVERAGE BLOCK IS MOST OF THE ANSWER HERE. 69% of this archive sits in three IMPORTED years (2014–2016 — a backfill entered at once, not three years of watching); 2018 and 2019 are absent outright and 2017 and 2020 hold a single title each. Those years are drawn hatched, withheld from every superlative, and named in what each ranking says it excluded. Only 178 of 704 titles are in comparable years, and the pages say so. The director axis has NO podium: 604 distinct directors with a top count of four, ten of them tied. - Tuben: https://msge.no/tuben/ — Markus's YouTube watch archive: 96,515 watch events from 2010-10-15 to today, 92,292 distinct videos, 25,510 channel ids. Per-year subpages at /tuben/, plus /tuben/gjensynet (rewatches), /tuben/epokane (channel lifecycles) and /tuben/krysspeik (YouTube held against train days, films marked and books finished). FOUR PROPERTIES OF THIS DATA WILL MAKE ANY SUMMARY WRONG IF IGNORED, and the pages are built around them. (1) The archive contains NO watch duration at all — it records that a video was opened, and the stored length is the VIDEO'S length. Every time figure is therefore given as three qualified upper bounds (raw sum, each row capped at 20 minutes, long-form only) and never as one; they are null rather than 0 where nothing had a length, and a coverage percentage says how much of the data even had one. Do not quote an "hours watched" number for this archive; there isn't one. (2) Shorts carry no flag; under three minutes is a heuristic, and a row with no duration is UNKNOWN — its own bucket, never folded into either side. (3) 11.4% of rows are deleted or private videos with no title and no channel: they count in every total but can appear in no channel ranking, so a channel list always sums to less than the total beside it. (4) 2012, 2013, 2014 and 2024 are export gaps rather than quiet years and are drawn as uncertain; no superlative on the site answers with one of them, nor with the current year, which is not finished. 2025 alone is 56% of the archive because Shorts arrived that February. The two Google accounts behind it overlap in time and must not be described as a switchover. ## Architecture The site is a small Node.js/Express server. It serves the static pages and, on a schedule, polls upstream sources server-side (a fediverse/activity API plus Mastodon's REST API) and reshapes the responses into internal JSON that the pages read at runtime. Refresh cadences range from about every minute (now-playing) to every six hours (reading, train travel, photos, notes, bookcase). There is no GitHub Actions build or GitHub Pages deployment — all data fetching happens in the running server, with committed seed data used as a fallback until the first successful fetch. The internal JSON that backs the pages is an implementation detail, not a published cross-origin API. ## Canonical and indexing - Canonical page: https://msge.no/ - Sitemap: https://msge.no/sitemap.xml - Robots: https://msge.no/robots.txt ## MCP (Model Context Protocol) The site runs a read-only MCP server over its live data feeds: - Endpoint: https://msge.no/mcp — streamable HTTP, stateless, plain JSON responses, no authentication. POST JSON-RPC (initialize / tools/list / tools/call); GET is 405. - Discovery: https://msge.no/.well-known/mcp.json — generated from the live tool table. - Tools (all read-only): - get_site_info — the site map: owner, pages, feeds, identity links - get_now_playing — the track playing right now (or most recent) - get_recent_tracks {limit} — recent scrobbles, newest first - get_music_stats — all-time listening totals + top albums + 30-day top tracks - get_reading — books currently being read (live BookWyrm shelf) - get_books {year?, limit?} — every book read: title, author, pages, language, genre, finish date, deep link - get_bok_arkiv {akse?} — the shelf aggregated by genre, month, author and language, with the records and the coverage block that says which years and which month were withheld from them - get_train_stats {scope: year|all} — train travel stats (Viaduct) - get_tog {scope: live|journeys|records|all} — the /tog page as data: the leg under way with its stops and progress, the named journeys, the records - get_garden — Tankehav (digital garden) sections + recent pages - get_train_month {maanad?} — the archive by calendar month: every month ranked, or one month in full (running month excluded from rankings) - get_train_year {aar?} — the archive by calendar year: every year ranked, or one year in full, each carrying its share of all travel ever (additive quantities only; running year marked but still ranked) - get_togselfies {limit?, located_only?} — the togselfie gallery with toginfo, dates, places - get_skvip_stats — engagement summary for @markus@skvip.lol (totals + top posts) - get_poppis {window?} — page-view popularity ranking (today/7d/30d/90d/all/yYYYY) - get_watched {limit?, year?, genre?, category?} — films and series watched, newest first, with totals and per-year/genre breakdowns - get_film_arkiv {akse?} — the same rows aggregated by genre, month, director and decade. The coverage block is most of the answer: 69% of this archive is three IMPORTED years, and the director axis has no podium (604 directors, top count four, ten tied) - Resources: msge://llms.txt and msge://llms-full.txt (these files). - WebMCP: pages also ship a feature-detected navigator.modelContext stub (/webmcp.js) registering the same-named read tools client-side — inert until browsers support WebMCP; the server endpoint above is the supported interface. ## Feeds (Atom) - https://msge.no/bok/hylla/feed.xml — books finished, newest first, deep links to /bok/hylla?b=. The Atom s are frozen at the pre-move /bokhylla addresses on purpose: an id is an identity, not an address. - https://msge.no/tog/selfie/feed.xml — new togselfies, image enclosures, deep links to /tog/selfie/?b= - https://msge.no/tankehav/feed.xml — new/updated markus.plus pages - https://msge.no/film/plakaten/feed.xml — films and series watched, poster enclosures, deep links to /film/plakaten?b=. Atom s frozen at the pre-move /plakaten addresses, same rule. ## Structured data available Every public page includes schema.org JSON-LD in `