Governance as Living Infrastructure
Here’s a simple way to think about governance like infrastructure—something you can design, version, and maintain, not just “navigate.”
Governance as Infrastructure (quick, practical playbook)
1) Layers (treat like an OSI stack)
- Policy layer: purpose, scope, legal basis, definitions.
- Roles layer: who can propose/approve/execute/audit (with clear separation of duties).
- Process layer: state machines for decisions (propose → review → decide → implement → verify → retrospect).
- Data layer: canonical records (schemas, IDs, timestamps, signatures, retention).
- Interface layer: how humans & systems interact (forms, APIs, dashboards).
- Assurance layer: logs, proofs, disclosures, audits, incident response, postmortems.
2) Composable roles (minimal set, reusable)
- Owner: sets goals, delegates authority.
- Steward: runs the process, ensures timeliness.
- Reviewer: checks completeness & risk.
- Approver: accountable decision-maker.
- Operator: executes the decision.
- Auditor/Observer: verifies and reports externally.
Tip: map people to roles per process, not globally. One person can hold multiple roles—just not conflicting ones in the same decision.
3) Predictable procedures (small, explicit state machines)
Define a one‑page spec per recurring decision:
- Trigger: what starts it (thresholds, cadence, incident).
- Inputs: required fields & attachments (with schema).
- States: draft → in‑review → approved/denied → implemented → verified → closed.
- SLA: max days per state.
- Controls: required reviewers/approvers, conflict checks.
- Outputs: artifacts produced (decision memo, change log, public notice).
- Disclosure: what is published, when, and where.
4) Standard disclosures (make them inspectable)
For each decision, publish a small, structured bundle:
- Decision card: ID, title, summary, rationale, risks, alternatives considered.
- Diff: what changed vs previous baseline.
- Ledger refs: timestamps, signatures, hash anchors (if applicable).
- Metrics impact: what you expect to move and how you’ll check.
5) Version everything (like code)
- Policy & playbooks in a repo (semver tags:
policy@1.2.0). - Change requests as PRs with reviewers & CI checks (lint schemas, verify signatures, run “impact checklist”).
- Changelogs readable by non‑engineers.
6) Small, reusable schemas (JSON-first)
entity.json(jurisdiction/org),role.json,process.json,decision.json,disclosure.json,control.json,risk.json.- Include: IDs, timestamps (UTC), actors (role + key), links, hash anchors, retention rules.
7) Controls you can actually run
- Pre‑commit checks: completeness, conflict-of-interest, privacy flags.
- Dual‑control: sensitive steps require 2 independent approvals.
- Thresholds: auto‑escalate when money/risk exceeds X.
- Kill‑switch: defined rollback path with authority and SLA.
8) Operating cadence (drumbeat)
- Weekly ops review: stuck items, SLA breaches, upcoming deadlines.
- Monthly quality review: random sample audits, metrics drift.
- Quarterly retro: retire unused processes, simplify top 10 friction points.
9) Minimal metrics (don’t overfit)
- Latency: median days from proposal → decision.
- Throughput: decisions/month by type.
- Rework rate: % sent back from review.
- Disclosure coverage: % decisions with complete public bundle.
- Control failures: count & mean‑time‑to‑detect.
10) Starter kit (cut‑paste templates)
- RACI map: one page per process.
- Decision memo: 7 prompts (problem, options, risks, cost, benefits, equity, recommendation).
- Public notice: 150‑word plain‑language summary + link to bundle.
- Incident postmortem: timeline, root cause, controls added, follow‑ups with owners/dates.
How this plugs into your work (fast paths)
- City/County workflows: encode procurement, budget amendments, data releases, and policy updates as state machines with public “decision cards.”
- Open data & analytics: publish decision bundles with machine‑readable schemas; your dashboards can compute latency/rework automatically.
- Blockchain anchors (BSV): hash the bundle + artifacts; store hash, timestamp, and signers’ keys in the disclosure for verifiable provenance.
- Product teams (Rails/Elixir): treat each process as a resource with states; expose REST/LiveView + webhooks for downstream notices.
One‑day setup (MVP)
- Pick one recurring decision (e.g., “publish dataset” or “approve vendor invoice > $10k”).
- Write a 1‑page process spec and RACI.
- Make a decision memo template + disclosure schema (JSON).
- Track 5 metrics above.
- Run it for two weeks; remove one step that didn’t add value.
If you want, tell me the first process you’d pilot (procurement change, data release, budget variance, policy update, incident response), and I’ll hand you a filled-in state machine, RACI, JSON schema, and the public disclosure template.
Here’s a simple, practical idea you might like: add a “Governance Ledger Appendix” to your org’s bylaws/operating agreement that logs proposals, roles, and votes as signed JSON‑LD entries, each anchored to BSV. It makes transparency programmable (APIs instead of PDFs) and verifiable (hash + signatures + on‑chain timestamp), without adding bureaucratic drag.
What this gives you (in plain terms)
- Tamper‑evidence: Each record is hashed; the hash is anchored to BSV’s public ledger with a timestamp.
- Portable structure: JSON‑LD uses schema.org-like vocabularies, so your data is self‑describing and easy to ingest.
- Programmable governance: Proposals, quorums, eligibilities, and results become machine‑readable; you can build dashboards, webhooks, and audits.
- Low overhead: You keep your normal meetings and votes—just emit a signed JSON blob per event and anchor it.
Minimal JSON‑LD shape
json
{
"@context": [
"https://schema.org",
{"gov": "https://example.org/governance#"}
],
"@type": "gov:Proposal",
"identifier": "prop-2025-001",
"name": "Adopt 2026 Budget",
"dateCreated": "2025-12-10T09:30:00Z",
"proposedBy": {"@type": "Person", "name": "Alex Rivera", "identifier": "did:web:org.example/alex"},
"description": "Approve the 2026 operating budget.",
"eligibility": {"@type": "gov:Eligibility", "role": "Member", "quorum": 0.6},
"ballots": [
{
"@type": "gov:Ballot",
"voter": "did:web:org.example/pat",
"vote": "YES",
"timestamp": "2025-12-10T09:45:04Z",
"signature": {
"type": "JsonWebSignature2020",
"creator": "did:web:org.example/pat#keys-1",
"jws": "eyJhbGciOiJFZERTQSJ9..."
}
}
],
"result": {"@type": "gov:Tally", "yes": 7, "no": 2, "abstain": 1, "passed": true},
"anchors": [
{
"@type": "gov:BSVAnchor",
"txid": "a3f...9c2",
"hash": "sha256-8d74...e1b",
"timestamp": "2025-12-10T09:46:11Z",
"network": "mainnet"
}
]
}
How it runs (tiny workflow)
- Draft → Sign: Generate the JSON‑LD for a proposal/ballot; sign with JWK (Ed25519/Secp256k1).
- Hash → Anchor:
sha256(record)→ embed hash in a tiny BSV transaction (OP_RETURN or similar) and broadcast. - Store originals: Keep the signed JSON in S3/GCS/Git (immutable path). The chain stores the proof, not the file.
- Verify later: Recompute hash, fetch the BSV tx, check timestamp and inclusion; verify signatures.
Governance appendix (human‑readable clause to paste)
Ledger Appendix — Governance Records The Organization shall publish machine‑readable governance records (proposals, ballots, tallies, role changes) as signed JSON‑LD. For each record, the Organization shall: (a) compute a SHA‑256 digest; (b) anchor the digest to the Bitcoin SV blockchain; (c) retain the signed original in durable storage; and (d) publish a public index of anchors and retrieval links. In the event of dispute, the canonical record is the signed JSON with a digest matching an anchor recorded on-chain prior to the disputed event’s resolution.
Practical tips (from experience)
- Keys & roles: Issue per‑member signing keys; rotate via “RoleChange” entries. Keep an admin recovery key in a hardware wallet.
- IDs: Use stable identifiers (e.g.,
did:web:org.example/alex) so signatures and roles remain linkable over time. - Privacy: Ballots can be public, pseudonymous, or delayed—choose per‑proposal. You can publish tallies immediately and ballots after N days.
- APIs: Expose a
/governance.jsonindex with pointers to each signed record + BSV txid. This powers dashboards and audits.
Tiny starter checklist
- [ ] Pick vocab: schema.org + a small
gov:extension (eligibility, ballot, tally, anchor). - [ ] Generate and distribute member keys; document revocation.
- [ ] Write a 200‑line script: create JSON‑LD → sign → hash → anchor to BSV → upload → update index.
- [ ] Add the appendix to your operating docs; announce the change.
- [ ] Ship a read‑only “Governance Log” page in your app/site.
If you want, I can sketch:
- a Tailwind‑clean “Governance Log” page,
- an Elixir (Phoenix + bsv‑ex) or Ruby script to sign/anchor, and
- a micro
gov:JSON‑LD context so this is plug‑and‑play with your BSV tooling.
Here’s a punchy idea worth stealing: treat public meeting minutes like an append‑only log you can query and verify, not static PDFs.
What that means (plain English)
- Append‑only: entries can only be added, never altered. Corrections are new entries linked to the originals.
- Queryable: citizens, staff, and courts can filter by date, topic, vote, member, ordinance, etc.
- Verifiable: every entry has a tamper‑evident fingerprint (hash) and a public audit trail.
Why it’s better than PDFs
- Trust: one canonical history; edits are transparent (no “revised PDF” mystery).
- Utility: easy cross‑meeting searches (“all motions about zoning §12.4, 2021–2025”).
- Automation: agendas → minutes → actions → code updates become machine‑readable events.
- Compliance by design: retention, sunshine laws, and discovery get simpler.
Minimal product spec
- Data model:
Meeting → AgendaItem → Motion/Vote → Attachment → EventLog. - Event log: immutable stream (
meeting.created,agenda_item.added,vote.recorded,minutes.corrected), each event hashed; daily hash anchor to a public ledger (fits your BSV focus). - Storage: Postgres (row‑level immutability via event‑sourcing), S3/GCS for attachments, content hashes for deduping.
- API: read‑only GraphQL/REST: filter by date range, member, topic, statute, tags.
- Public UI: table + faceted search, diff views for corrections, permalinks for every event.
- Exports: CSV/JSON/Atom feeds; printable “minutes” generated from the log at any time.
Governance & ops (the unsexy, important part)
- Roles: Clerk (author), Counsel (legal review), Auditor (periodic verification), Public (read‑only).
- Workflow: draft → review → adopt → publish; corrections are append‑only with reason codes.
- Controls: versioned policy registry (what must be recorded; correction windows; redaction rules).
- Audit: monthly third‑party verification that regenerate ledger anchors from raw events; publish proof.
- Continuity: nightly off‑site encrypted backups + open export so another vendor can take over.
Adoption playbook (pilot → policy)
- Seed a pilot with 3–6 months of recent meetings; dual‑publish: PDFs + log UI.
- Policy memo: declare the log the “official system of record,” PDFs as human‑readable renderings.
- Clerk UX: make entry as fast as writing a PDF (templates, keyboard shortcuts, bulk upload).
- Stakeholder wins: one‑click “What changed since last meeting?” and auto‑draft “Actions required.”
- Sunshine hooks: auto‑FOIA bundles: scoped exports with cryptographic receipts.
- Statute alignment: retain existing legal signatures; add hash receipts to record the exact text adopted.
Quick tech sketch (fits your stack)
- Elixir/Phoenix + Commanded (event‑sourcing)
- Postgres (events, projections)
- LiveView (clerk console & public browse)
- BSV‑EX (daily anchor of Merkle root of events)
- USWDS/Tailwind (accessible UI)
First milestone (2–3 weeks of focused work)
- CRUD for meetings, agenda items, motions, votes → emitted as events
- Public browse with filters + printable minutes renderer
- Daily hash anchor job + verification page
- CSV/JSON exports; permalinked records
Nice extras
- Semantic tags (ordinance codes, departments) for smarter search
- Embeddable widgets (e.g., “latest actions for Parks & Rec”)
- Webhook outbox (notify websites, codifiers, and GIS when relevant items pass)
If you want, I can drop a starter Phoenix schema + Commanded aggregates/events and a simple anchoring job so you can plug it into your Jurisdictional stack.
Here’s a simple way to frame “open data” so it actually drives decisions and participation: think of it as a continuous semantic enrichment pipeline—ingest → standardize → contextualize → embed → serve → act.
The pipeline at a glance
- Ingest: pull data from source systems (exports, scrapers, forms, APIs). Capture provenance (who/when/how) every time.
- Standardize: clean + validate + map to shared schemas (e.g., OpenReferral, GTFS, Schema.org). Use controlled vocabularies and IDs, not free‑text.
- Contextualize: add domain meaning—join to reference tables (jurisdictions, agencies, programs), tag with policies, funding lines, and legal constraints; record data rights/licensing.
- Embed: create vector embeddings for search, clustering, and similarity so humans (and LLMs) can find and relate things beyond exact matches.
- Serve: publish durable interfaces—versioned files, stable APIs, SPARQL/SQL endpoints, vector search, and human-friendly catalogs. Include changelogs and webhooks.
- Act: close the loop—dashboards, alerts, workflows, comment/flag channels, request-for-correction flows, and decision memos linking back to the exact records used.
Why this helps (beyond “just publish the CSV”)
- Keeps data actionable (each step adds clarity, not just storage).
- Makes datasets discoverable (schema + embeddings power search and reuse).
- Enables participatory governance (context + feedback loops + provenance).
- Reduces vendor lock‑in (standards + versioned outputs + portable IDs).
Minimal starter checklist (repeat each release)
- Ingest: source URL, method, schedule, checksum.
- Standardize: schema map, validation report, canonical IDs.
- Contextualize: jurisdiction/agency IDs, policy tags, license, retention.
- Embed: model used, embedding store, update cadence.
- Serve: files (CSV/Parquet), API & docs, catalog entry, webhook.
- Act: feedback form, issue tracker label, change log, adoption notes.
Participation hooks you can add Day 1
- “Propose a correction” and “Ask a question about this record.”
- Public “data used in this decision” links in meeting agendas.
- Lightweight RFP/RFI companion packs: sample data, schemas, and test queries.
- Monthly “diff notes” (what changed, why it matters, who should care).
Governance guardrails (keep it boring and reliable)
- Data steward of record + escalation path.
- Versioning policy (semantic: MAJOR.MINOR.PATCH) and deprecation windows.
- Reproducible pipelines (scripts + containers) and published QA checks.
- Privacy review + suppression rules logged alongside releases.
If you’d like, I can turn this into a one‑page playbook (with Tailwind‑ready sections) or a tiny Rails/Phoenix template that ships: a catalog page, a dataset schema map, an embeddings-backed search, and a “decision memo” component that cites exact records.
Here’s a crisp idea you can use right away: publish procurement, legislation, and policy metadata as schema.org JSON‑LD on public websites to create “civic trust APIs” that both humans and machines can audit.
Why this matters (quick hits)
- Zero new endpoints: JSON‑LD sits in your existing HTML, instantly crawlable and cacheable.
- Low‑friction audits: Third parties can verify facts (amounts, vendors, votes, amendments) without scraping brittle HTML.
- Future‑proof: Works with search, LLMs, and open‑data portals; keeps your canonical record close to the source page.
- Sunshine by default: Machine‑readable fields line up with public‑records obligations (dates, amounts, signers, versions).
What to publish (minimum viable fields)
- Procurement: notice ID, buyer, supplier, line items, amounts, funding source, status, key dates, documents, contract file hash.
- Legislation: bill ID, title, sponsors, status, readings/votes timeline, amendments, links to authoritative PDFs + content hash.
- Policy/Regulation: policy ID, effective/expiry dates, jurisdiction, authority, scope, impacted systems, revision history.
How to publish (tiny checklist)
- Put a single
<script type="application/ld+json">block on each detail page. - Use stable IDs/URLs; include
isBasedOn(source doc) andidentifier(internal system ID). - Version your records with
dateModifiedand link prior versions viaisPartOf/hasPart. - Add hashes of files (
sha256) inencoding→contentUrl+ customdigestfield (extensions are okay). - Make a simple “/metadata” index (sitemap or NDJSON) for bulk discovery.
Example: Procurement award (JSON‑LD)
json
{
"@context": "https://schema.org",
"@type": "GovernmentService",
"name": "Solid Waste Pickup – FY2026 Award",
"identifier": "RFP-2025-014-AWARD",
"serviceOperator": {
"@type": "GovernmentOrganization",
"name": "City of Example – Public Works",
"identifier": "EXA-DPW"
},
"provider": {
"@type": "Organization",
"name": "Acme Disposal, Inc.",
"identifier": "DUNS:123456789"
},
"offers": {
"@type": "Offer",
"priceCurrency": "USD",
"price": "2185000",
"eligibleRegion": "US-CA",
"businessFunction": "Provision",
"validFrom": "2025-07-01",
"validThrough": "2026-06-30"
},
"additionalProperty": [
{"@type":"PropertyValue","name":"fundingSource","value":"General Fund"},
{"@type":"PropertyValue","name":"awardMethod","value":"Competitive RFP"}
],
"isBasedOn": {
"@type": "CreativeWork",
"name": "Executed Contract PDF",
"url": "https://city.example.gov/contracts/RFP-2025-014-award.pdf",
"encodingFormat": "application/pdf",
"identifier": "sha256:3f1c...ab9e"
},
"datePublished": "2025-12-01",
"dateModified": "2025-12-05",
"url": "https://city.example.gov/procurement/RFP-2025-014/award"
}
Example: Legislative bill (JSON‑LD)
json
{
"@context": "https://schema.org",
"@type": "Legislation",
"identifier": "ORD-2025-112",
"name": "Open Data & Digital Rights Ordinance",
"jurisdiction": "US-CA-ExampleCity",
"datePublished": "2025-11-18",
"legislationType": "Ordinance",
"legislationPassedDate": "2025-12-02",
"legislationApplies": "Municipal Departments",
"sponsor": [{"@type":"Person","name":"Councilmember Rivera"}],
"hasPart": [{
"@type":"VoteAction",
"actionStatus":"CompletedActionStatus",
"startTime":"2025-12-02",
"result":"passed",
"participant":[{"@type":"Organization","name":"City Council"}],
"object": {"@type":"Legislation","identifier":"ORD-2025-112"},
"additionalProperty":[
{"@type":"PropertyValue","name":"ayes","value":7},
{"@type":"PropertyValue","name":"nays","value":2}
]
}],
"workExample": [{
"@type":"CreativeWork",
"name":"Enrolled Text (PDF)",
"url":"https://city.example.gov/legislation/ORD-2025-112.pdf",
"identifier":"sha256:b7c9...42af"
}],
"url": "https://city.example.gov/legislation/ORD-2025-112"
}
Governance guardrails (keep it trustworthy)
- Change logs: publish diffs (who changed what, when, why).
- Reference hashes: include SHA‑256 for every authoritative file.
- Provenance: record
creator,accountablePerson, andapprovalsteps. - De‑dup keys: never recycle
identifiers; deprecate withexpires/validThrough.
Fast start for your stack
- Rails/Elixir: render JSON‑LD via a partial; attach to detail pages.
- Sitemaps: add
/metadata.ndjsonlisting URLs +dateModified. - Validation: add a unit test that parses pages and checks required fields.
- Docs page: “How to reuse our metadata” with examples + field dictionary.
If you want, I can tailor a drop‑in partial for Rails 7 and Phoenix 1.7+ (with tests) using your existing “Jurisdictional” data model.
Here’s a simple, practical way to think about “true transparency” in government procurement: don’t just publish PDFs—publish the whole process as a machine‑readable, participatory ledger from intent → contract → delivery → outcomes.
A civic “procurement ledger” (from end to end)
What it is (plainly): A public, append‑only record of each purchase, updated as work progresses. Think of it like shipment tracking, but for public money—every step is structured data you can query, audit, and discuss.
1) Core objects (minimal, stable schema)
- Opportunity: id, agency, program, problem statement, budget range, small‑biz set‑asides, timeline.
- Solicitation: id, linked opportunity, documents (URLs + hashes), Q&A threads, addenda (versioned).
- Bids/Proposals: bidder org, price(s), approach summary, compliance flags, evaluation metadata (criteria scores, rationale).
- Award/Contract: vendor, amount(s), line items, milestones, SLAs, payment terms, change orders (versioned).
- Performance: milestone reports, acceptance checks, deliverable links + hashes, incident logs, extensions.
- Payments: obligation vs. actuals, invoice ids, dates, amounts, funding source, cumulative burn vs. budget.
- Outcomes: KPI targets vs. actuals, user satisfaction, uptime/quality metrics, post‑implementation review.
All objects carry:
id,status,created_at,updated_at,publisher, and a tamper‑evident content hash.
2) Minimal API surface (read/write)
GET /opportunities?agency=...&status=openGET /solicitations/{id}(returns JSON + file hashes)POST /bids(structured fields + attachments with checksums)GET /awards/{id}/milestonesPOST /contracts/{id}/change_ordersGET /payments?contract_id=...GET /outcomes?program=...&fiscal_year=...
Include webhooks (e.g., “milestoneaccepted”, “invoicepaid”) so the public site, watchdogs, and researchers can subscribe.
3) Integrity + trust (lightweight, real)
- Checksums for every file (PDF, CSV, images); publish the SHA‑256 next to each link.
- Signed snapshots: nightly exports (NDJSON/CSV) + manifest file; sign the manifest and publish.
- Public anchors (optional): anchor the manifest hash to a public chain for an immutable timestamp.
- Versioning: never overwrite—append new versions; expose diffs.
4) Participation that’s actually useful
- Bidder Q&A: public, searchable, with clear deadlines and auto‑notify subscribers.
- Public comment windows: on solicitation drafts and on large change orders.
- Vendor performance profiles: per‑vendor aggregate KPIs, delivery history, and dispute outcomes.
- Civic subscribers: let press, researchers, and vendors follow agencies, categories, or thresholds.
5) Simple vendor performance model (start here)
- On‑time delivery rate (milestones)
- Acceptance rate on first submission
- SLA compliance (e.g., uptime, response time)
- Change‑order ratio (% of contract value)
- Issue density (substantiated incidents / quarter) Expose raw counts + definitions to avoid gaming.
6) Procurement “ledger math” (useful rollups)
- Lead time: RFP published → award.
- Delivery velocity: milestones accepted / month.
- Cost drift: (current total) – (original award) / original award.
- Payment latency: invoice received → paid. All computable from the objects above.
7) Governance + ops (how to start without boiling the ocean)
- Ownership: finance/procurement co‑own; IT runs the pipeline; the records officer signs the nightly manifest.
- Publishing SLA: each event must hit the public API within N days (e.g., 3 business days).
- Data quality playbook: required fields, validation rules, and a weekly error report that’s public.
- Redaction policy: narrowly defined (privacy, trade secrets); auto‑log every redaction with a reason code.
- Change control: schema is semver’d; breaking changes require a deprecation window and migration notes.
8) File formats (keep it boring)
- JSON for APIs, CSV for bulk, NDJSON for snapshots.
- OpenRef: include a one‑page data dictionary with examples.
- IDs: stable, non‑recycled; link everything via ids, not names.
9) First 90‑day pilot (checklist)
- Pick one department + top 3–5 active contracts.
- Stand up
GETendpoints and nightly signed snapshots. - Publish: opportunities, solicitations, awards, milestones, payments.
- Add a vendor KPI page (even if sparse).
- Host a public “data hour” to gather feedback; fix one thing per week.
10) Why this matters (beyond “open data”)
- For procurement officials: faster audits, easier vendor due diligence, less email archaeology.
- For policy folks: real‑time spend and outcomes to steer programs.
- For politicians: credible transparency (you can point to the ledger, not a press release).
- For vendors: clearer expectations, fewer surprises, leveled playing field.
- For the public: traceability from promise to payment to results.
If you want, I can draft:
- a minimal JSON schema (v1) you can drop into a Rails/Phoenix app,
- a seed dataset + sample API responses,
- and a one‑pager for agency leadership to approve a 90‑day pilot.
Here’s a simple, robust way to make your RAG stack self‑healing against silent data drift: fingerprint every file and chunk, store the hashes with your vectors, and re‑hash on retrieval to verify nothing changed.
Why this matters (quickly)
- Detects drift/corruption: If a source doc or chunk changes, the SHA‑256 won’t match—flag it before using.
- Auditable: Hashes act like tamper‑evident seals (per the Secure Hash Standard, SHA‑256).
- Cheap + portable: Works with any store (Qdrant, SQLite‑vec, Postgres) and any embedding model.
Minimal metadata model
For each chunk you index:
source_path(URI or logical id)byte_range(start, end) orchunk_indexfile_sha256(whole file)chunk_sha256(exact text you embedded)model,embedding_dim,created_at
Store alongside vectors
Qdrant (payload-friendly)
json
{
"id": "uuid",
"vector": [/* embedding */],
"payload": {
"source_path": "s3://bucket/report.pdf",
"byte_range": [1024, 2047],
"file_sha256": "…",
"chunk_sha256": "…",
"model": "text-embedding-3-large",
"created_at": "2025-12-10T00:00:00Z"
}
}
SQLite‑vec (add metadata columns)
sql
CREATE VIRTUAL TABLE chunks USING vec0(
embedding float[1536]
);
CREATE TABLE chunk_meta (
id INTEGER PRIMARY KEY,
source_path TEXT,
byte_start INTEGER,
byte_end INTEGER,
file_sha256 TEXT,
chunk_sha256 TEXT,
model TEXT,
created_at TEXT
);
-- Keep ids aligned between tables (or store rowid in chunk_meta).
Index (ingest) workflow
- Read file → compute
file_sha256. - Split into chunks (stable splitter) → compute
chunk_sha256on exact chunk text. - Embed → upsert vector + metadata.
- Optionally keep
original_chunk_textin cold storage for forensic diffing.
Elixir sketch
```elixir def sha256(bin), do: :crypto.hash(:sha256, bin) |> Base.encode16(case: :lower)
def ingest(filepath) do file = File.read!(filepath) file_hash = sha256(file) chunks = MySplitter.split(file)
for {text, {startpos, endpos}} <- chunks do chunkhash = sha256(text) emb = MyEmbedder.embed(text) Qdrant.upsert(%{ vector: emb, payload: %{ sourcepath: filepath, byterange: [startpos, endpos], filesha256: filehash, chunksha256: chunkhash, model: "text-embedding-3-large", createdat: DateTime.utcnow() |> DateTime.to_iso8601() } }) end end ```
Ruby (Rails) sketch
```ruby require "digest"
def sha256(s) = Digest::SHA256.hexdigest(s)
def ingest(path) file = File.binread(path) file_hash = sha256(file)
splitintochunks(file).each do |text, (startpos, endpos)| chunkhash = sha256(text) emb = Embeddings.embed(text) Qdrant.upsert( vector: emb, payload: { sourcepath: path, byterange: [startpos, endpos], filesha256: filehash, chunksha256: chunkhash, model: "text-embedding-3-large", createdat: Time.now.utc.iso8601 } ) end end ```
Retrieval guardrail (verify before use)
- Fetch top‑k candidates (vectors + payload).
- Recompute
chunk_sha256on the current chunk text from source (or cached original). - If mismatch → drop/flag; optionally auto‑re‑embed and upsert a fresh record.
- Only pass verified text to the LLM.
Pseudocode
python
results = qdrant.search(query_vec, top_k=8)
verified = []
for r in results:
text = load_text(r.payload["source_path"], r.payload["byte_range"])
if sha256(text) == r.payload["chunk_sha256"]:
verified.append((text, r.score))
else:
flag_drift(r.id) # log, queue re-embed
return verified[:k]
Practical tips
- Stable chunking: Don’t let trivial whitespace changes churn hashes. Normalize line endings; pick a deterministic splitter.
- Migrations: If you change split rules or embedding model, bump a
schema_versionand keep old points until rebuilt. - Performance: Hashing is fast; the I/O to re‑read source may dominate—cache recent source blobs.
- Audits: Keep a small “chain of custody” log:
{source_path, file_sha256, ingested_at, tool_version}. - Fallback: If the original source is gone, the mismatch itself is a valuable signal—treat the chunk as untrusted.
If you want, I can drop this into your Rails and Phoenix projects with ready‑to‑run modules (Qdrant + SQLite‑vec), plus a tiny “re‑embed on drift” job.
By Ryan Wold · © 2025–2026 Ryan Wold
Licensed CC BY-NC 4.0. AI training requires a license — machine-readable terms.
Tip: $afomi on HandCash · afomi@handcash.io