# SolidHaus — Household Inventory Management on Solid + RDF **Project Codename:** SolidHaus **Author:** Christopher (toph) **Date:** 2026-02-26 **Status:** Design / Brainstorm Phase **Version:** 2.0 --- ## 1. Vision & Goals SolidHaus is a **local-first, multi-user, Solid-compatible household inventory management system** that lets you track every physical item in your home using semantic web technologies (RDF, schema.org, Linked Data). Items are identified by short 7-character auto-generated IDs (nanoid), which are pre-printed as QR codes on label sheets. The app scans these codes to log sightings, locations, and usage — building a living knowledge graph of your possessions. ### Core Principles - **Data Sovereignty**: All data lives in your Solid Pod (Community Solid Server / CSS). You own it. - **Local-First + Multi-User**: Works offline, syncs between household members via CRDT (Automerge). No cloud dependency for core operations. - **Semantic Web Native**: Uses schema.org vocabulary + custom ontology extensions. Data is RDF (JSON-LD / Turtle). - **Scan-First UX**: Optimized for walking around with a phone, scanning barcodes, and quickly updating item locations. - **Pre-Printed Labels**: Batch-generate QR code sheets with pre-allocated IDs. Peel, stick, scan to associate. - **Check-In / Check-Out**: Items have a custody state — tracked from storage to active use and back. - **From Tools to Pantry**: Supports durable goods, consumables, and perishables with quantity tracking. --- ## 2. Architecture Overview ``` ┌─────────────────────────────────────────────────────┐ │ SolidHaus App │ │ (SvelteKit + Capacitor, native on Android/iOS) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌───────────────────┐ │ │ │ Scanner │ │ Inventory│ │ Label Sheet │ │ │ │ (Camera) │ │ Browser │ │ Generator (PDF) │ │ │ └────┬─────┘ └────┬─────┘ └────────┬──────────┘ │ │ │ │ │ │ │ ┌────▼──────────────▼──────────────────▼──────────┐ │ │ │ Local Data Layer │ │ │ │ (IndexedDB + Automerge CRDT) │ │ │ └────────────────────┬────────────────────────────┘ │ │ │ │ │ ┌────────────────────▼────────────────────────────┐ │ │ │ Sync Layer │ │ │ │ Automerge-repo (WebSocket) + Solid RDF export │ │ │ └────────────────────┬────────────────────────────┘ │ └───────────────────────┼──────────────────────────────┘ │ ┌─────────────┼──────────────┐ ▼ ▼ ┌─────────────────┐ ┌──────────────────┐ │ Automerge Sync │ │ Solid Pod │ │ Server (NixOS) │ │ (CSS / NSS) │ │ (WebSocket) │ │ /inventory/ │ └─────────────────┘ └──────────────────┘ ``` ### Technology Stack | Layer | Technology | Rationale | |-------|-----------|-----------| | **Framework** | SvelteKit 2 (Svelte 5 with runes) | Compiler-first, minimal overhead, excellent DX | | **Native Wrapper** | Capacitor | Android/iOS from single codebase, native API access | | **Build** | Vite (built into SvelteKit) | Fast, modern bundler | | **Local Storage** | IndexedDB (via idb) | Reliable offline storage for structured data | | **CRDT / Sync** | Automerge + automerge-repo | Conflict-free multi-user sync between household members | | **RDF/Solid** | @inrupt/solid-client, @inrupt/solid-client-authn-browser | Solid Pod interaction, RDF serialization | | **Barcode Scanning (native)** | @capacitor-mlkit/barcode-scanning | ML Kit — fast, supports all formats on Android/iOS | | **Barcode Scanning (web)** | Barcode Detection API + ZXing fallback | Native browser API on Chrome, polyfill for others | | **Barcode Rendering** | bwip-js | Generate barcode/QR images for label sheets | | **ID Generation** | nanoid (custom alphabet) | Short, URL-safe, collision-resistant IDs | | **Styling** | Tailwind CSS 4 | Rapid mobile-first UI development | | **State Management** | Svelte 5 runes ($state, $derived, $effect) | Built-in, no external library needed | | **Desktop (future)** | Tauri 2 | Rust backend, officially supports SvelteKit | --- ## 3. Data Model — Schema.org + Custom Ontology ### 3.1 Core Types We use `schema:IndividualProduct` as the base type for tracked items. This is the correct schema.org type for "a single, identifiable product instance (e.g., a laptop with a particular serial number)." #### Item (schema:IndividualProduct) ```turtle @prefix schema: . @prefix solidhaus: . @prefix xsd: . a schema:IndividualProduct ; schema:identifier "za3rbam" ; schema:name "Bosch Drill PSB 1800" ; schema:description "Cordless drill, green, bought at Bauhaus" ; schema:category "Tools" ; schema:brand [ a schema:Brand ; schema:name "Bosch" ] ; schema:image ; schema:serialNumber "SN-483920" ; schema:purchaseDate "2024-03-15"^^xsd:date ; schema:color "Green" ; # Custom properties for inventory tracking solidhaus:shortId "za3rbam" ; solidhaus:itemType solidhaus:Durable ; solidhaus:barcodeFormat "qr" ; solidhaus:barcodeUri "https://haus.toph.so/za3rbam" ; # Location tracking (latest state) solidhaus:lastSeenAt ; solidhaus:lastSeenTimestamp "2026-02-20T14:30:00Z"^^xsd:dateTime ; solidhaus:supposedToBeAt ; solidhaus:locationConfidence "confirmed" ; # Custody state solidhaus:custodyState solidhaus:CheckedIn ; solidhaus:storageTier solidhaus:HotStorage . ``` #### Location (schema:Place) ```turtle a schema:Place ; schema:name "Werkstatt" ; schema:description "Basement workshop" ; solidhaus:locationType "room" ; solidhaus:defaultStorageTier solidhaus:HotStorage ; schema:containedInPlace . ``` #### Sighting Event (schema:Action) ```turtle a solidhaus:SightingEvent, schema:FindAction ; schema:object ; schema:location ; schema:startTime "2026-02-20T14:30:00Z"^^xsd:dateTime ; schema:agent ; solidhaus:sightingType "scan" ; solidhaus:confidence "confirmed" . ``` ### 3.2 Custom Ontology: solidhaus **Namespace:** `https://vocab.toph.so/solidhaus#` Full ontology defined in `solidhaus.ttl`. Key terms: | Term | Type | Description | |------|------|-------------| | `shortId` | Property | 7-char nanoid identifier | | `barcodeUri` | Property | URI encoded in barcode | | `itemType` | Property | durable, consumable, perishable, etc. | | `custodyState` | Property | checked-in or checked-out | | `storageTier` | Property | hot, warm, or cold | | `lastSeenAt` | Property | Where item was last observed | | `supposedToBeAt` | Property | Designated storage location | | `checkedOutReason` | Property | in-use, lent, in-repair, etc. | | `currentQuantity` | Property | Amount remaining (consumables) | | `quantityUnit` | Property | ml, g, pcs, %, etc. | | `lowThreshold` | Property | Alert threshold | | `CheckOutEvent` | Class | Subclass of schema:CheckOutAction | | `CheckInEvent` | Class | Subclass of schema:CheckInAction | ### 3.3 Item Types & Lifecycle ```typescript type ItemType = | 'durable' // Tools, furniture, electronics — long-lasting | 'consumable' // Batteries, tape, screws — deplete with use | 'disposable' // Paper plates, trash bags — single use | 'perishable' // Food/drink with expiry dates | 'media' // Books, DVDs, games | 'clothing' // Wearable items | 'document' // Papers, manuals, certificates | 'container' // Boxes, bins — items that hold other items ; ``` Consumables and perishables have quantity tracking: ```typescript interface QuantityInfo { currentQuantity: number | null; // e.g., 750 originalQuantity: number | null; // e.g., 1000 quantityUnit: string; // "ml", "g", "pcs", "sheets", "%" lowThreshold: number | null; // alert when below (e.g., 100) expiryDate: string | null; // ISO date, for perishables } ``` ### 3.4 Location Hierarchy Locations form a tree using `schema:containedInPlace`: ``` Home (Residence) ├── Erdgeschoss (Floor) │ ├── Küche (Room) │ │ ├── Schublade Links (Drawer) │ │ └── Regal über Spüle (Shelf) │ ├── Wohnzimmer (Room) │ │ └── TV-Schrank (Furniture) │ └── Flur (Room) ├── Obergeschoss (Floor) │ ├── Schlafzimmer (Room) │ ├── Büro (Room) │ │ ├── Schreibtisch (Furniture) │ │ └── Regal (Shelf) │ └── Bad (Room) └── Keller (Floor) └── Werkstatt (Room) ├── Werkbank (Furniture) └── Werkzeugwand (Storage) ``` ### 3.5 Custody Model — Check-In / Check-Out Items have a **custody state**: | State | Meaning | Icon | |-------|---------|------| | **Checked In** | At designated storage location, put away | ✅ 📥 | | **Checked Out** | Removed from storage | 🔄 📤 | Check-out reasons: | Reason | Example | |--------|---------| | **In Use** | Drill on workbench, book being read | | **In Transit** | Carrying tools to another room | | **Lent** | Lent to neighbor Max (records *who*) | | **In Repair** | Sent to repair shop | | **Temporary** | Moved for a project | | **Consumed** | Used up or disposed of (terminal) | #### RDF Example ```turtle solidhaus:custodyState solidhaus:CheckedOut ; solidhaus:checkedOutSince "2026-02-26T10:00:00Z"^^xsd:dateTime ; solidhaus:checkedOutReason solidhaus:InUse ; solidhaus:checkedOutFrom ; solidhaus:lastSeenAt ; solidhaus:supposedToBeAt ; solidhaus:storageTier solidhaus:HotStorage . ``` ### 3.6 Storage Tiers | Tier | Name | Examples | Usage | |------|------|----------|-------| | 🔴 **Hot** | Active / Grab-and-go | Pegboard, desk, kitchen counter | Daily | | 🟡 **Warm** | Nearby / Effort required | Cabinet, toolbox, high shelf | Weekly | | 🔵 **Cold** | Deep / Archival | Attic boxes, basement bins | Seasonal | For cold storage, `storageContainer` and `storageContainerLabel` track which box to dig out. Containers themselves can be tracked items. ### 3.7 Scan Actions (Context-Dependent) **If item is Checked In:** "Check Out" | "Just Sighting" | "Move Storage" **If item is Checked Out:** "Check In" | "Still Using" | "Change Location" Confidence decays: ≤30d → confirmed, ≤90d → likely, ≤180d → assumed, >180d → unknown. Overdue items (checked out >7d) surface on dashboard. --- ## 4. ID Generation & URI Strategy ### 4.1 Nanoid Configuration ```typescript import { customAlphabet } from 'nanoid'; const ALPHABET = '23456789abcdefghjkmnpqrstuvwxyz'; // no 0/o/1/i/l const generateItemId = customAlphabet(ALPHABET, 7); // Examples: "za3rbam", "k7xhw2n", "p4fvt8c" ``` 30^7 ≈ 21.8 billion combinations. Lowercase for URI readability. ### 4.2 URI Scheme **Canonical QR content:** `https://haus.toph.so/{id}` Deep linking behavior: 1. **Native app installed (Capacitor):** App Links / Universal Links → opens app 2. **PWA installed on Android:** Chrome intercepts → opens PWA 3. **Nothing installed:** Opens browser → landing page Additionally register `haus://` as custom scheme in Capacitor for direct launch. ### 4.3 ID Parsing ```typescript export function parseItemId(rawValue: string): string | null { const httpsMatch = rawValue.match(/haus\.toph\.so\/([23456789a-hjkmnp-z]{7})$/); if (httpsMatch) return httpsMatch[1]; const schemeMatch = rawValue.match(/^haus:\/\/([23456789a-hjkmnp-z]{7})$/); if (schemeMatch) return schemeMatch[1]; const rawMatch = rawValue.match(/^[23456789a-hjkmnp-z]{7}$/); if (rawMatch) return rawMatch[0]; return null; } ``` ### 4.4 Pre-Printed Label Sheets Batch-generate QR code sheets with unassigned IDs. Apply labels physically, then scan to associate. **Workflow:** 1. Generate 50 new IDs → stored as "unassigned" in DB 2. Download PDF (A4 sticker sheet) or print tape strip 3. Apply labels to items 4. Scan label → "Create new item with ID za3rbam?" → enter details --- ## 5. Multi-User Offline-First Sync ### 5.1 Architecture: Automerge CRDT ``` Device A ←→ Automerge Sync Server (NixOS) ←→ Device B │ ▼ Solid Pod (periodic RDF export) ``` Each device maintains a local Automerge document. automerge-repo handles sync via WebSocket relay. Offline changes accumulate locally and merge conflict-free when reconnected. Solid Pod serves as long-term RDF archive, not real-time sync layer. ### 5.2 Automerge Document Structure ```typescript interface InventoryDoc { items: { [shortId: string]: Item }; locations: { [id: string]: Location }; sightings: { [id: string]: Sighting }; preGeneratedIds: { [id: string]: PreGeneratedId }; } ``` ### 5.3 Auth & Permissions - Household members authenticate with Solid WebID - Sync server: shared-secret or Solid-based auth - Personal items can be `visibility: 'private'` ### 5.4 NextGraph (Future) CRDT-native RDF with E2EE, P2P sync, SPARQL. Could replace Automerge + Solid if it reaches production. --- ## 6. Solid Pod Structure ``` /inventory/ ├── ontology.ttl ├── items/ │ ├── za3rbam.ttl │ └── k7xhw2n.ttl ├── locations/ │ ├── home.ttl │ └── werkstatt.ttl ├── sightings/ │ └── 2026-02/ │ └── s_x7k9m2.ttl └── photos/ └── za3rbam_01.jpg ``` --- ## 7. Barcode Scanning ### 7.1 Capacitor ML Kit (Primary) ```typescript import { BarcodeScanner, BarcodeFormat } from '@capacitor-mlkit/barcode-scanning'; const { barcodes } = await BarcodeScanner.scan({ formats: [BarcodeFormat.QrCode, BarcodeFormat.Code128, BarcodeFormat.DataMatrix], }); ``` ### 7.2 Web Fallback ```typescript if ('BarcodeDetector' in window) { const detector = new BarcodeDetector({ formats: ['qr_code', 'code_128', 'data_matrix'] }); const barcodes = await detector.detect(videoFrame); } // Fallback: html5-qrcode (ZXing WASM) for Safari/Firefox ``` ### 7.3 Deep Linking (Capacitor) AndroidManifest.xml registers `haus://` scheme and `https://haus.toph.so` App Links. iOS Info.plist registers URL scheme + Associated Domains. --- ## 8. Label Generation & Printing ### 8.1 Pre-Generated ID Batches IDs are allocated in batches (e.g., 50), stored as unassigned until scanned and linked to an item. ### 8.2 PDF Label Sheets Generate A4 PDFs using Canvas API + jsPDF. Each cell: QR code + human-readable ID. Print on sticker paper. ### 8.3 Print Server (NixOS, Optional) REST API for label tape printing. Accepts label data over HTTP, handles the printer protocol. Works from any device on LAN. --- ## 9. App Screens & User Flows ### 9.1 Main Screens 1. **Dashboard** — Stats, recent sightings, overdue items, low-stock consumables 2. **Scanner** — Camera + barcode detection 3. **Item Detail** — Info, photos, history, quantity 4. **Item Create/Edit** — Form with photo capture 5. **Location Browser** — Hierarchical tree 6. **Search** — Full-text search 7. **Label Sheets** — Generate and manage batches 8. **Settings** — Solid Pod, sync, household members ### 9.2 Key Flows #### Scan & Check Out ``` Scan item → Card shows: ✅ Checked In at Pegboard (Hot) → Tap "Check Out" → reason: "In Use" → location: Workbench → 🔄 Checked Out (In Use) from Pegboard → at Workbench ``` #### Scan & Check In ``` Scan item → Card shows: 🔄 Checked Out since 2h ago → Tap "Check In" → confirm return to Pegboard → ✅ Checked In at Pegboard ``` #### Scan Pre-Printed Label → New Item ``` Scan unknown QR → ID recognized as pre-generated, unassigned → "Create new item?" → fill form → save → label associated ``` #### Quick Update Consumable ``` Scan olive oil → "400ml / 1000ml (40%)" → Quick: "Almost empty" | "Half left" | "Just opened" → Below threshold → "Add to shopping list?" ``` #### Walking Audit ``` Start Audit for "Büro" → shows expected items → Scan each → confirmed ✅ → Summary: 12/15 confirmed, 3 missing ``` --- ## 10. Implementation Phases ### Phase 1: Core MVP (2-3 weeks) - [ ] SvelteKit + Capacitor + Tailwind scaffold - [ ] IndexedDB data layer (items, locations, sightings) - [ ] Item create/edit with photo capture - [ ] 7-char nanoid + QR generation (bwip-js) - [ ] Scanner (Capacitor ML Kit + Barcode Detection API fallback) - [ ] Location hierarchy, check-in/check-out, sighting logging - [ ] Basic search, offline support ### Phase 2: Multi-User Sync (1-2 weeks) - [ ] Automerge doc structure + automerge-repo - [ ] NixOS sync server, multi-device sync ### Phase 3: Solid Integration (1-2 weeks) - [ ] Solid auth, RDF serialization, periodic export - [ ] Pod structure, ACLs, ontology at vocab.toph.so ### Phase 4: Label Sheets (1 week) - [ ] Pre-gen ID batches, A4 PDF generator - [ ] Unassigned → item association flow - [ ] Print server (NixOS, optional) ### Phase 5: Consumables & Pantry (1 week) - [ ] Item types, quantity tracking, low-stock alerts, expiry dates ### Phase 6: Advanced (2+ weeks) - [ ] Audit mode, confidence decay, stats, categories/tags, import/export ### Phase 7: Future - [ ] NextGraph, NFC tags, desktop via Tauri 2, product barcode lookup --- ## 11. Key Dependencies ```json { "dependencies": { "@inrupt/solid-client": "^2", "@inrupt/solid-client-authn-browser": "^2", "@automerge/automerge": "latest", "@automerge/automerge-repo": "latest", "@automerge/automerge-repo-network-websocket": "latest", "@automerge/automerge-repo-storage-indexeddb": "latest", "@capacitor/core": "^6", "@capacitor-mlkit/barcode-scanning": "^6", "nanoid": "^5", "idb": "^8", "bwip-js": "^4", "date-fns": "^4", "jspdf": "^2" }, "devDependencies": { "@sveltejs/adapter-static": "latest", "tailwindcss": "^4", "@tailwindcss/vite": "latest", "typescript": "^5.7" } } ``` --- ## 12. Project Structure ``` solidhaus/ ├── src/ │ ├── routes/ │ │ ├── +layout.svelte # App shell, bottom nav │ │ ├── +layout.ts # SSR disabled │ │ ├── +page.svelte # Dashboard │ │ ├── scan/+page.svelte │ │ ├── items/ │ │ │ ├── +page.svelte │ │ │ ├── new/+page.svelte │ │ │ └── [id]/+page.svelte │ │ ├── locations/+page.svelte │ │ ├── labels/+page.svelte │ │ └── settings/+page.svelte │ ├── lib/ │ │ ├── components/ # .svelte files │ │ ├── data/ # IndexedDB layer │ │ ├── sync/ # Automerge + Solid export │ │ ├── solid/ # Solid auth, RDF, pod │ │ ├── scanning/ # Barcode detection + parsing │ │ ├── printing/ # Label PDF gen, print server │ │ ├── stores/ # Svelte 5 rune stores │ │ ├── types/ # TypeScript types │ │ ├── ontology/ # Namespace constants │ │ └── utils/ # ID gen, confidence, formatting │ └── app.html ├── static/ ├── ontology/solidhaus.ttl ├── android/ # Capacitor ├── ios/ # Capacitor ├── svelte.config.js ├── capacitor.config.ts ├── vite.config.ts └── package.json ``` --- ## 13. Schema.org Actions ```turtle a schema:UseAction ; schema:object ; schema:agent ; schema:startTime "2026-02-20T14:30:00Z" ; schema:location . a schema:MoveAction ; schema:object ; schema:fromLocation ; schema:toLocation ; schema:startTime "2026-02-21T09:00:00Z" . ``` --- ## 14. Design System **Dark theme (slate-based):** bg slate-900, surface slate-800, primary blue-500, confirmed emerald-500, warning amber-500, danger red-500. **Mobile:** touch targets ≥44px, bottom sheet modals, haptic on scan (Capacitor Haptics), large monospace for IDs. --- ## 15. References - **Solid**: solidproject.org, docs.inrupt.com, CommunitySolidServer - **Schema.org**: IndividualProduct, Place, FindAction, CheckInAction, CheckOutAction - **SvelteKit + Capacitor**: svelte.dev/docs/kit, capacitorjs.com, v2.tauri.app - **CRDT**: automerge.org, nextgraph.org - **Barcode**: MDN BarcodeDetector, bwip-js, html5-qrcode