En plattform för grottkataloger på webben – öppen för alla, med fördjupning för medlemmar.
AI-åtkomst (MCP): mcp.speleotastic.com/mcpDu besöker Speleotastic.
Speleotastic är en gemensam plattform som visar samma data på två domäner: grottor.se på svenska och speleotastic.com på engelska. Domänen avgör språk och vilken variant som visas utan prefix i URL:en.
Innehållet är öppet för alla, men känsliga uppgifter och medlemsmaterial kräver inloggning. Resten av sidan visar hur det fungerar – med grottan som genomgående exempel.
Grottan (modell ca) är den centrala entiteten. Här är Lummelundagrottan – live från API:et.
En grotta har fält som kod, namn, längd (med möjligt +-suffix och mätnoggrannhet), vertikal utbredning (djup), grottyp, status och svårighetsgrad. Fälten till vänster förklarar dem; till höger renderas den riktiga grottan med samma komponent som på grottsidan.
Lummelundagrottan är Gotlands längsta grotta (~4000 m) och en karstgrotta (typ KG).
Varje entitet och fil ligger i exakt en katalog. Du ser bara det dina behörigheter tillåter.
Som gäst kan du läsa 0 grottor.
Katalogen bestäms per entitet (entity.catalogId) och per fil (file.catalogId) och versionshanteras aldrig. All läsning begränsas till de kataloger dina behörigheter (permissions.R) ger.
Behörighet ärvs additivt genom en grupphierarki: Publikt → Inloggade → SSF Medlemmar → SSF Administratörer → Superadministratörer. En gäst på grottor.se läser katalogerna 4 och 20.
Enskilda dokumentsidor kan ha egen katalog: den effektiva sidbehörigheten är filePage.catalogId ?? file.catalogId.
En bastabell (entity) plus 12 modelltabeller bildar ett träd – varje nod med egen katalog.
agAgentrotcaCaverotdoDocumentrotwpWebpagerotptPointcoCoordinatesgeGeotbTextblockrfReferencerlRelationdfDocument fileenEntrycaCaveptPointcoCoordinatestbTextblockrfReferencerlRelationVarje nod (punkt, koordinat, textblock, referens …) har sin egen katalog.
Tabellen entity håller id, tvåbokstavsmodell, variant och katalog. Varje modell (ca, do, pt, co, tb, rf, rl …) har en egen tabell. Föräldern anges med parentEntityId + parentModel; en grotta kan även vara barn till en annan grotta.
Cache-fält som cacheRootEntityId och isRoot gör att hela grottans träd kan hämtas snabbt.
Testa själv: växla inloggning och ändra kataloger, och se vad en gäst respektive medlem ser.
ptIngång (punkt)coKoordinat ungefärligt lägetbBeskrivningrfReferensÄndra katalog på en nod eller växla inloggning – synligheten uppdateras direkt.
Det här är en simulering i webbläsaren – inga data skrivs. Regeln är densamma som på riktigt: en entitet syns när dess katalog finns i din läsmängd.
Den hemliga grottan ligger i en medlemskatalog och försvinner helt för gäster. Koordinaten visas ungefärligt när du saknar rätt att läsa den.
Varje ändring sparas. Tre tillstånd styrs av tre kolumner.
| draftUserId | 12 |
| revisionId | — |
| toRevisionId | — |
Ägs av en användare (draftUserId), ännu inte publicerad.
| draftUserId | — |
| revisionId | 87 |
| toRevisionId | — |
Publicerad, gällande version.
| draftUserId | — |
| revisionId | 54 |
| toRevisionId | 87 |
Tidigare version, ersatt av en nyare revision.
Varje modelltabell kan ha flera rader för samma entitet, åtskilda av draftUserId, revisionId och toRevisionId (plus den genererade kolumnen headEntityId som pekar ut aktuell version).
När ett utkast publiceras skapas en revision (agentModel='ag' för aktören, plus createdBy för användaren), utkastet blir HEAD och den gamla versionen får sitt toRevisionId satt.
En variant är ett innehållsnamnrum. Den avgör bl.a. vilka grottyper som finns.
KGKarstgrottaSGStrandgrottaEVGÖvrig erosions-/vittringsgrottaRGRasbrantsgrottaFGFörskjutningsgrottaBGBlockgrottaXGÖvrig grottaKFKarstfenomenej grottaLIGrottliknande bildningej grottaBYByggd grottaej grottaXEÖvrig ej grottaej grottaXXOkänd typej grottaGSSprickgrottahistoriskFrågor och svarsalternativ (question/questionOption) är nycklade på (modell, variant, kod). En grotta i variant ssf kan bara använda ssf:s grottyper.
Varje alternativ bär en markörfärg (används på kartan) och en flagga för om det räknas som en riktig grotta. XX är standard; GS (sprickgrotta) användes historiskt t.o.m. 2014.
Ändringar i gamla Basen hämtas automatiskt varje natt.
Ett OS-cron (tools/scripts/basen-daily.sh, inte ett Cloudflare-cron) kör stegen efter att Basen uppdaterats. Importen stagar utkast som användare 20 i en MariaDB-klon och EXCEPT-diffar mot HEAD innan den skrivs till D1.
Commit publicerar utkasten som revisioner per (aktör, dag) och stänger borttagna. Grottor, bilagor, kartor, källhänvisningar och personer (som aktörer) importeras – enbart variant ssf.
Dokument, filer och sidor – med behörighet ända ner på sidnivå.
0 dokument & kartor i katalogen.
Servern sätter ihop en PDF med bara de sidor du får läsa.
Fysiska filer (file) och sidor (filePage) versionshanteras inte; dokumentet (do) och kopplingen till filer (df) gör det. Effektiv sidbehörighet är filePage.catalogId ?? file.catalogId.
När du öppnar en PDF bygger media-workern om den med enbart de sidor din behörighet tillåter – resten lämnar aldrig servern.
Alla grottor på en snabb vektorkarta med sök och filter.
Kartsidan (/map) hämtar hela grottdatasetet, så här laddas det bara på begäran. Datat är katalogfiltrerat precis som allt annat.
Kartan bygger på ett lager per typ (exakta, ungefärliga, filtrerade, markerade) ovanpå en gemensam källa.
Utan rätt att läsa koordinaten får du bara en grov cell – aldrig det exakta läget.
Endast en grov H3-cell (~22 km) visas.
För varje grotta beräknas två celler: en exakt (H3 res 15) och en grov (res 4, ~22 km), plus koordinatens katalog (cacheCoordCatalogId).
Saknar du den katalogen returneras bara den grova cellen (approximate:true) och vilken katalog som skulle låsa upp den (missingCatalogId). Det exakta läget lämnar aldrig servern.
En grottas koordinat ger automatiskt dess administrativa områden.
Geo-workern H3-indexerar områdenas polygoner (interiör- och gränsceller + ray-casting) och baklängesgeokodar varje grottas cachade koordinat till entityArea (första per typ markeras som huvud).
Föräldrar expanderas automatiskt: kommun → län, socken → härad.
Samma data, för AI-assistenter – med samma behörighetsregler.
list-catalogssearchlist-areasget-caveget-documentread-documentEndast läsning – samma katalogbehörighet som webben.
En skrivskyddad MCP-server (mcp.speleotastic.com) exponerar grott- och dokumentdata. Inloggning sker via en OAuth 2.1-server (PKCE) på hub-domänen.
Access-token (15 min, HS256) bär dina läsbara kataloger (p.R) begränsade till scope mcp:read. Koordinater döljs/approximeras precis som på webben.