Skip to content
bartoszkobylinskiPublic

Repository files navigation

lovverk

lovverk is a Git-versioned Markdown corpus of current Norwegian laws and central regulations, generated from Lovdata's public-data API by the open-source lovspor engine. It is intended for AI/RAG ingestion, legal research, change tracking, and MCP-based access from assistants such as Claude. It is an unofficial derivative and does not replace Lovdata.

Corpus status

Generated by lovspor from the manifests on every sync and promotion; anything between these markers is overwritten.

Dataset Current documents Published under Manifest generated
lover/ 753 Lovdata public data, NLOD 2.0 2026-11-01
forskrifter/ 5,135 Lovdata public data, NLOD 2.0 2026-11-01
lokale-forskrifter/ 29 åndsverkloven § 14 2026-10-20

lover/ and forskrifter/ together hold 5,888 current documents; removed documents stay in manifest.json as tombstones and are not counted.

lokale-forskrifter/ holds 29 local regulations from 6 authorities, published on the basis of åndsverkloven § 14 and not asserted to be in force: kommune 1848 (1), kommune 3101 (1), kommune 3118 (1), kommune 4646 (1), kommune 5025 (23), kommune 5059 (2).

What this repository is

  • Every current Norwegian lov (law) and sentral forskrift (central regulation) as one Markdown file with YAML front matter — the Published Rendering of the source document, deterministically rendered from Lovdata's XML.
  • Every change in the source data lands as a Git commit. This repository's Git history is the authoritative record of published corpus states — what the corpus said, and when. It is not an authoritative record of Norwegian law; for legal content, Lovdata is authoritative.
  • Per-document change history, per-section embedding vectors, and dataset indexes are published alongside the documents.
  • A separate dataset, lokale-forskrifter/, for local regulations observed on kommune and fylkeskommune websites — not from Lovdata, not under NLOD; see Local regulations. It holds 29 regulations from 6 kommuner as of 2026-10-20 and grows as regulations are promoted.

This repository contains generated corpus artifacts and control metadata. The code that produces the corpus — ingestion, rendering, sync, embedding generation, search, and the MCP server — lives in lovspor. There is no application code here; the corpus artifacts are generated rather than hand-maintained.

Production. Auto-synced daily at 04:00 UTC. The corpus tracks every current lov and sentral forskrift — 5,984 documents (764 lover + 5,220 sentrale forskrifter) as of 2026-10-17 (the root manifest's generated_at). Counts change daily; each dataset's INDEX.md carries its live count, and manifest.json is the authoritative statement of current membership of the two Lovdata datasets. Local regulations are a separate dataset with their own manifest — see Local regulations.

Repository contents

lovverk/
├── lover/                          # current Norwegian laws
│   ├── INDEX.md                    # generated index of current acts (live count)
│   ├── <slug>.md                   # one document per act: the Published Rendering
│   ├── history/<slug>.json         # per-document change history — structured source of truth
│   ├── history/<slug>.md           # the same history as a human-readable derived view
│   └── embeddings/<slug>.bin       # per-section embedding vectors (LSPE v2, int8-quantized)
├── forskrifter/                    # current central regulations (same layout)
├── lokale-forskrifter/             # local regulations observed on kommune/fylkeskommune websites
│   ├── manifest.json               # this dataset's own membership file (see below)
│   └── <authority_id>/<slug>.md    # one document per regulation, per enacting authority
└── manifest.json                   # authoritative membership of lover/ and forskrifter/ + per-document metadata
  • <slug>.md — the rendered legal text with YAML front matter (identity, title, retrieval provenance, EU/EEA basis, NLOD attribution).
  • history/<slug>.json — structured per-document event history (added / updated / renamed / removed, with dates and commits). The structured source of truth for generated history.
  • history/<slug>.md — a human-readable view derived from the JSON. If they ever disagree, the JSON wins.
  • embeddings/<slug>.bin — per-section vectors for semantic search. Derived artifacts, regenerable from the Markdown; never a legal source.
  • manifest.json — one record per document; see below.
  • History files for repealed documents are deliberately kept: they are the audit trail that an act existed and was removed.

Authority and versioning

Four things are authoritative for four different questions, and they do not substitute for each other:

  • Lovdata is authoritative for Norwegian legal source text.
  • This repository's Git history is authoritative for published corpus states — it is the corpus's version store, not housekeeping. Historical states are intentionally retained, lovspor's temporal tools (get_law_at, list_law_versions, diff_law_versions) answer directly from it, and canonical history is never force-pushed or rewritten; doing so would break the version model, so recovery from a bad sync is git revert, never a rewrite. The generated history/ files are derived projections of this history, convenient to read but never a replacement for it.
  • The manifests are authoritative for current corpus membership and per-document control metadata, each for its own dataset: the root manifest.json for lover/ and forskrifter/, lokale-forskrifter/manifest.json for local regulations. File presence alone never determines membership.
  • The Published Renderings are evidence of what this project published — a lossy, unofficial derivative, not canonical legal text.

Nothing derived — an embedding, a search result, a rendered Markdown file, a manifest field — independently establishes what the law legally means.

Manifest

manifest.json holds one record per document, current and removed. The fields that matter most to a consumer:

  • status — "current" or "removed". A removed record is a tombstone: the document's identity, path, title and source hash are preserved, removed_reason says why (null = it left the upstream dataset; "upstream_placeholder" = Lovdata still lists it but serves an error notice instead of legal text, so the corpus withholds it), and its history files remain on disk as the audit trail. Tombstones are not current, searchable legal documents.
  • xml_hash — SHA-256 of the document's normalized source XML. Change detection keys on this hash, never on the rendered Markdown.
  • renderer_version — which renderer produced the on-disk Markdown bytes.
  • embedding_hash — the xml_hash the document's .bin was built from; a mismatch means the vectors are stale and get rebuilt on the next keyed sync.
  • embedding_space / embedding_space_id — the Embedding Space Identity of the sidecar (see below).

The full schema with exact semantics is documented in lovspor/docs/data-model.md.

Embeddings and ESI

Each document carries a binary sidecar embeddings/<slug>.bin in the LSPE v2 format: a 16-byte header (magic LSPE, version 2, section count, dimension, dequantization scale), then the 16-byte raw ESI digest, then one int8-quantized 3072-dimensional vector per § section of the act, keyed by section id. Long sections are stored as several chunk records under one id. A document whose rendering yields no embeddable sections legitimately has a header-only sidecar with zero vectors.

Version 1 is the same header with no ESI digest. The lovspor engine still reads it, but this corpus does not ship it: every sidecar published here is version 2.

ESI (Embedding Space Identity) identifies the embedding configuration — provider, model, dimension, endpoint — that produced a corpus artifact. Semantic consumers must only compare query and corpus vectors that belong to the same embedding space: cosine similarity across two spaces returns confident-looking nonsense, not an error. The lovspor engine enforces this — its semantic_search refuses documents whose recorded identity differs from, or is unknown to, the configured embedder, and reports every exclusion.

Two facts about where identity lives, and they are load-bearing:

  • The sidecar carries its own identity. The 16-byte ESI digest sits in the file, so a .bin read on its own tells you which embedding space produced it — no manifest lookup required. This is ADR-0005 Stage 2, live: every sidecar in this corpus is version 2. A version 1 sidecar carries no identity and cannot answer that question, which is why a mixed corpus is forbidden rather than merely discouraged.
  • Identity is also recorded in the manifest (embedding_space + embedding_space_id), stamped at generation time by the embedder that actually produced the vectors. Every current record carries its recorded identity, and new or changed documents receive theirs through the normal daily sync.

The exact identity definition, canonical serialization, and compatibility rules are owned by lovspor/docs/embeddings.md.

Using the corpus

Direct use — clone the repository and read it. The Markdown is plain text with YAML front matter; git log --follow on any file is that act's publication history; manifest.json tells you what is current.

Programmatic / search / MCP use — use the lovspor engine (Python, on PyPI); it handles corpus parsing, semantic-compatibility enforcement, temporal retrieval, quote verification, and serves it all over MCP:

uvx lovspor fetch-corpus     # clone the corpus to the local cache
claude mcp add lovverk -- uvx lovspor mcp

Sixteen read-only tools: search (keyword and semantic), section-level retrieval, per-act history, point-in-time text, diffs, citation validation, and verbatim-quote verification. See lovspor/docs/mcp.md. Semantic search additionally needs an operator-supplied OpenAI key; everything else runs fully local.

What you should not infer from this corpus: that a rule does not exist because it is absent here (the corpus covers acts and central regulations only — no circulars, court practice or forarbeider; local regulations only as far as they have been observed and promoted into lokale-forskrifter/, which is no register of them); that a retrieval hit answers a legal question (it locates text, nothing more); or that historical corpus states describe when a provision was legally in force (Git history records what the corpus held on a date — corpus-retrieval time, not legal-validity time).

How updates work

The lovspor engine runs on a daily schedule. It downloads the current tarballs from Lovdata's public-data API, normalizes and hashes each document's XML, compares against manifest.json, and commits only documents whose source content changed — one commit per changed document, plus index/manifest bookkeeping. Renderer-only migrations are committed under migration: subjects and are deliberately excluded from per-document change history.

Verification and reproducibility

Every document records the SHA-256 of its normalized source XML in manifest.json, and every published state of the corpus is a Git commit. You can verify:

  • integrity of the corpus against its own manifest (paths, hashes, membership);
  • the full provenance of any line of any document via git log / git blame;
  • the upstream source itself, by downloading the same public tarballs from https://api.lovdata.no/v1/publicData/get/;
  • the rendering end-to-end: the engine is public and AGPL-3.0-licensed, so a third party can obtain the upstream data, run lovspor independently, and compare the generated artifacts and hashes against this repository. Rendering is deterministic — same source in, identical bytes out.

Relationship to lovspor

lovspor (public, AGPL-3.0) is the engine; lovverk (this repository) is its published output. The only normal write paths for corpus artifacts into this repository are the engine's: its scheduled sync for lover/, forskrifter/ and the root manifest, and its local-regulation promotion (lovspor promote …) for lokale-forskrifter/ — neither writes the other's files. Consuming the corpus never requires the engine — a clone plus manifest.json is a complete, self-describing dataset — but everything programmatic (search, MCP, temporal tools, verification) lives on the engine side.

Local regulations (lokale-forskrifter/)

lokale-forskrifter/ is a separate dataset for local regulations (lokale forskrifter) enacted by kommuner and fylkeskommuner. As of 2026-10-20 (its manifest's generated_at) it holds 29 regulations from 6 kommuner — Steigen (1848), Halden (3101), Indre Østfold (3118), Fjaler (4646), Røros (5025) and Orkland (5059) — and it grows as the engine promotes more. The engine's promotion commands (lovspor promote …) are the only write path into this directory.

  • Where the text comes from. Lovdata's public-data API does not serve local regulations. A regulation reaches this directory by one of two routes, and its front matter says which (basis): observed (basis: "observed") — captured from the enacting authority's own website by lovspor's local-law observatory, with observed_at_first and source_url; or received (basis: "received") — the text the kommune sent in answer to an access request under offentleglova, with received_at, the request id in innsyn_request and the form the text arrived in (received_form). Of the 29 regulations, 3 are observed and 26 received. In both routes only an artifact classified as an enacted regulation is promoted — never a hearing, case document, protocol or individual decision. Nothing in this directory is Lovdata data.
  • Basis for publication: åndsverkloven § 14. Laws, regulations and other decisions by public authorities are not subject to copyright (åndsverkloven § 14), and the local regulations here are published on that basis. They are not published under NLOD 2.0: that is the licence under which Lovdata distributes its data, and it applies to lover/ and forskrifter/ only. Every document states source_license: "åndsverkloven § 14" in its front matter, and the integrity check refuses any file in this directory that mentions NLOD.
  • Observed, not asserted. A document here records that this text was retrievable from the authority's website when it was observed, or that the authority sent it in answer to a request — nothing more. It has not been checked against Norsk Lovtidend or Lovdata, and this project does not assert that the regulation is in force, current, complete or the authoritative version. Every document carries asserted: false whatever its basis. A local regulation missing from this directory may well exist.
  • ObservedAt provenance. Observation time has its own fields and is never fused with another time axis: observed_at_first in the front matter is the first observation of that version of the text, and <authority_id>/observations/<slug>.json holds each version's observation intervals and source URLs. Git commit dates record when the corpus took a version in, and are never backdated to the observation; vedtatt and ikraft are filled only where the regulation's text states them.
  • Identity and layout. Documents live at lokale-forskrifter/<authority_id>/<slug>.md, where authority_id is the SSB KLASS code of the enacting authority (four digits for a kommune, two for a fylkeskommune). The id is lf-yyyymmdd-nnnn when the regulation's own identification block states its date and number, otherwise a deterministic lk-<authority_id>-<hash>. (authority_id, slug) is unique; two kommuner may each have a renovasjonsforskrift.
  • A manifest of its own. lokale-forskrifter/manifest.json has the root manifest's shape (documents, generated_at, version), keyed by id; records carry authority_id, authority_type, content_hash (over the normalised extracted text — there is no XML, so no xml_hash), version and extractor_version. The root manifest.json never lists a local regulation, so tools and engines that read only the root manifest are unaffected by this dataset, and CI fails any change that writes lokale-forskrifter/ together with the root manifest, lover/ or forskrifter/.
  • Personal data is minimised under personopplysningsloven before anything is committed: only the regulation body is rendered, never page chrome, contact details or signatures, and a regulation whose body carries personal data is held for human review rather than published.

Source and attribution

Data source: Lovdata public-data API

  • https://api.lovdata.no/v1/publicData/get/gjeldende-lover.tar.bz2
  • https://api.lovdata.no/v1/publicData/get/gjeldende-sentrale-forskrifter.tar.bz2

Contains data under the Norwegian licence for Open Government data (NLOD) 2.0, distributed by Lovdata. The data has been converted to Markdown by the lovspor project.

License: NLOD 2.0 for the derived legal text in lover/ and forskrifter/ (including the generated history files); local regulations in lokale-forskrifter/ are published on the basis of åndsverkloven § 14, not NLOD (see Local regulations); CC0 1.0 for the repository structure, manifest schema and README. See LICENSE.

Limitations

This is a generated corpus and tooling project. Source fidelity and retrieval correctness are engineering properties, not legal advice — anyone needing authoritative legal interpretation should verify against Lovdata and qualified counsel. This project is not affiliated with Lovdata.

Releases

Packages

Used by

Contributors

Languages