# Silo: Item Database and Part Management System for FreeCAD **Version:** 0.1 Draft **Date:** January 2026 **Author:** Kindred Systems LLC --- ## 1. Overview Silo is an item database with configurable part number generation, designed for R&D-oriented workflows. It integrates with FreeCAD 1.0+ to provide git-like object management, revision tracking, and physical inventory location management. ### 1.1 Core Philosophy Silo treats **part numbering schemas as configuration, not code**. Multiple numbering schemes can coexist, each defined in YAML. The system is schema-agnostic—it doesn't impose a particular part numbering philosophy (intelligent vs. non-intelligent numbers) but instead provides the machinery to implement whatever scheme the organization requires. ### 1.2 Key Principles - **Items are the atomic unit**: Everything is an item (parts, assemblies, drawings, documents) - **Schemas are mutable**: Part numbering schemas can evolve, though migration tooling is out of scope for MVP - **Append-only history**: All parameter changes are recorded; item state is reconstructable at any point in time - **Configuration over convention**: Hierarchies, relationships, and behaviors are YAML-defined --- ## 2. Architecture ### 2.1 Components ``` ┌─────────────────────────────────────────────────────────────┐ │ FreeCAD 1.0+ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Silo Workbench (Python) │ │ │ │ - silo checkout / commit / status / log │ │ │ │ - Part number generation │ │ │ │ - Property sync with FreeCAD objects │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Silo Core (CLI/Library) │ │ - Schema parsing and validation │ │ - Part number generation engine │ │ - Revision management │ │ - Relationship graph │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────┴───────────────┐ ▼ ▼ ┌─────────────────────────┐ ┌─────────────────────────────┐ │ PostgreSQL │ │ MinIO │ │ (psql.kindred.internal)│ │ - .FCStd file storage │ │ - Item metadata │ │ - Versioned objects │ │ - Relationships │ │ - Thumbnails │ │ - Revision history │ │ │ │ - Location hierarchy │ │ │ └─────────────────────────┘ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Web UI (Browse/Search) │ │ - Item browser with hierarchy navigation │ │ - Search and filtering │ │ - "Open in FreeCAD" links (freecad:// URI handler) │ │ - BOM viewer │ └─────────────────────────────────────────────────────────────┘ ``` ### 2.2 Technology Stack | Component | Technology | Notes | |-----------|------------|-------| | Database | PostgreSQL | Existing instance at psql.kindred.internal | | File Storage | MinIO | S3-compatible, versioning enabled | | FreeCAD Integration | Python workbench | Macro-style commands | | CLI & API Server | Go (1.23) | chi/v5 router, pgx/v5 driver, zerolog | | Web UI | Go html/template + htmx | Lightweight, minimal JS | --- ## 3. Data Model ### 3.1 Items An **item** is the fundamental entity. Items have: - A **part number** (generated according to a schema) - A **type** (part, assembly, drawing, document, etc.) - **Properties** (key-value pairs, schema-defined and custom) - **Relationships** to other items - **Revisions** (append-only history) - **Files** (optional, stored in MinIO) - **Location** (optional physical inventory location) ### 3.2 Database Schema (Conceptual) ```sql -- Part numbering schemas (YAML stored as text, parsed at runtime) CREATE TABLE schemas ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT UNIQUE NOT NULL, version INTEGER NOT NULL DEFAULT 1, definition JSONB NOT NULL, -- parsed YAML created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); -- Items (core entity) CREATE TABLE items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), part_number TEXT UNIQUE NOT NULL, schema_id UUID REFERENCES schemas(id), item_type TEXT NOT NULL, -- 'part', 'assembly', 'drawing', etc. created_at TIMESTAMPTZ DEFAULT now(), current_revision_id UUID -- points to latest revision ); -- Append-only revision history CREATE TABLE revisions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), item_id UUID REFERENCES items(id) NOT NULL, revision_number INTEGER NOT NULL, properties JSONB NOT NULL, -- all properties at this revision file_version TEXT, -- MinIO version ID if applicable created_at TIMESTAMPTZ DEFAULT now(), created_by TEXT, -- user identifier (future: LDAP DN) comment TEXT, UNIQUE(item_id, revision_number) ); -- Item relationships (BOM structure) CREATE TABLE relationships ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), parent_item_id UUID REFERENCES items(id) NOT NULL, child_item_id UUID REFERENCES items(id) NOT NULL, relationship_type TEXT NOT NULL, -- 'component', 'alternate', 'reference' quantity DECIMAL, reference_designator TEXT, -- e.g., "R1", "C3" for electronics metadata JSONB, -- assembly-specific relationship config revision_id UUID REFERENCES revisions(id), -- which revision this applies to created_at TIMESTAMPTZ DEFAULT now() ); -- Location hierarchy (configurable via YAML) CREATE TABLE locations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), path TEXT UNIQUE NOT NULL, -- e.g., "lab/shelf-a/bin-3" name TEXT NOT NULL, parent_id UUID REFERENCES locations(id), location_type TEXT NOT NULL, -- defined in location schema metadata JSONB, created_at TIMESTAMPTZ DEFAULT now() ); -- Item inventory (quantity at location) CREATE TABLE inventory ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), item_id UUID REFERENCES items(id) NOT NULL, location_id UUID REFERENCES locations(id) NOT NULL, quantity DECIMAL NOT NULL DEFAULT 0, updated_at TIMESTAMPTZ DEFAULT now(), UNIQUE(item_id, location_id) ); -- Sequence counters for part number generation CREATE TABLE sequences ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), schema_id UUID REFERENCES schemas(id), scope TEXT NOT NULL, -- scope key (e.g., project code, type code) current_value INTEGER NOT NULL DEFAULT 0, UNIQUE(schema_id, scope) ); ``` --- ## 4. YAML Configuration System ### 4.1 Part Numbering Schema Schemas define how part numbers are generated. Each schema consists of **segments** that are concatenated with a **separator**. ```yaml # /etc/silo/schemas/kindred-rd.yaml schema: name: kindred-rd version: 3 description: "Kindred Systems R&D part numbering" # Separator between segments (default: "-") separator: "-" # Uniqueness enforcement uniqueness: scope: global case_sensitive: false segments: - name: category type: enum description: "Category code (2-3 characters)" required: true values: F01: "Hex Cap Screw" F02: "Socket Head Cap Screw" # ... 70+ categories across: # F01-F18: Fasteners # C01-C17: Fluid Fittings # R01-R44: Motion Components # S01-S17: Structural Materials # E01-E27: Electrical Components # M01-M18: Mechanical Components # T01-T08: Tooling and Fixtures # A01-A07: Assemblies # P01-P05: Purchased/Off-the-Shelf # X01-X08: Custom Fabricated Parts - name: sequence type: serial length: 4 padding: "0" start: 1 description: "Sequential number within category" scope: "{category}" format: "{category}-{sequence}" # Example outputs: # F01-0001 (first hex cap screw) # R27-0001 (first linear rail) # A01-0001 (first assembly) ``` > **Note:** The schema was migrated from a `{project}-{type}-{sequence}` format (v1) to `{category}-{sequence}` (v3). Projects are now managed as many-to-many tags on items rather than embedded in the part number. See `migrations/006_project_tags.sql`. ### 4.2 Segment Types | Type | Description | Options | |------|-------------|---------| | `string` | Fixed or variable length string | `length`, `min_length`, `max_length`, `pattern`, `case` | | `enum` | Predefined set of values | `values` (map of code → description) | | `serial` | Auto-incrementing integer | `length`, `padding`, `start`, `scope` | | `date` | Date-based segment | `format` (strftime-style) | | `constant` | Fixed value | `value` | ### 4.3 Serial Scope Templates The `scope` field in serial segments supports template variables referencing other segments: ```yaml # Sequence per category (current kindred-rd schema) scope: "{category}" # Global sequence (no scope) scope: null ``` ### 4.4 Alternative Schema Example (Simple Sequential) ```yaml # /etc/silo/schemas/simple.yaml schema: name: simple version: 1 description: "Simple non-intelligent numbering" segments: - name: prefix type: constant value: "P" - name: sequence type: serial length: 6 padding: "0" scope: null # global counter format: "{prefix}{sequence}" separator: "" # Output: P000001, P000002, ... ``` ### 4.5 Location Hierarchy Schema ```yaml # /etc/silo/schemas/locations.yaml location_schema: name: kindred-lab version: 1 hierarchy: - level: 0 type: facility name_pattern: "^[a-z-]+$" - level: 1 type: area name_pattern: "^[a-z-]+$" - level: 2 type: shelf name_pattern: "^shelf-[a-z]$" - level: 3 type: bin name_pattern: "^bin-[0-9]+$" # Path format path_separator: "/" # Example paths: # lab/main-area/shelf-a/bin-1 # lab/storage/shelf-b/bin-12 ``` ### 4.6 Assembly Metadata Schema Each assembly can define its own relationship tracking behavior: ```yaml # Stored in item properties or as a linked document assembly_config: # What relationship types this assembly uses relationship_types: - component # standard BOM entry - alternate # interchangeable substitute - reference # related but not part of BOM # Whether to track reference designators use_reference_designators: true designator_format: "^[A-Z]+[0-9]+$" # e.g., R1, C3, U12 # Revision linking behavior child_revision_tracking: specific # or "latest" # Custom properties for relationships relationship_properties: - name: mounting_orientation type: enum values: [top, bottom, left, right, front, back] - name: notes type: text ``` --- ## 5. FreeCAD Integration ### 5.1 Workbench Commands The Silo workbench provides toolbar commands in FreeCAD: | Command | Description | Status | |---------|-------------|--------| | `Silo_Save` | Auto-save document and upload to MinIO | Implemented | | `Silo_Commit` | Save with revision comment | Implemented | | `Silo_Pull` | Download item by part number / create new | Implemented | | `Silo_Push` | Batch upload modified files | Implemented | | `Silo_Info` | View revision history for current item | Implemented | | `Silo_Register` | Generate part number for current document | Implemented | | `Silo_Open` | Open item from Silo by part number | Implemented | | `Silo_Browse` | Browse items in a list dialog | Implemented | ### 5.2 Property Synchronization Silo properties map to FreeCAD custom properties: ```python # FreeCAD object properties (synced from Silo) obj.addProperty("App::PropertyString", "SiloPartNumber", "Silo", "Part number") obj.addProperty("App::PropertyString", "SiloRevision", "Silo", "Current revision") obj.addProperty("App::PropertyString", "SiloDescription", "Silo", "Item description") # ... additional properties as defined in schema ``` ### 5.3 File Storage Strategy FreeCAD `.FCStd` files are ZIP archives. Storage options: 1. **Whole file storage** (MVP): Store complete .FCStd in MinIO with versioning 2. **Exploded storage** (future): Unpack and store components separately for better diffing For MVP, whole file storage is simpler and MinIO versioning handles history. ### 5.4 Checkout Locking (Future) MVP operates as single-user. Future multi-user support will need locking strategy: - **Pessimistic locking**: Checkout acquires exclusive lock - **Optimistic locking**: Allow concurrent edits, handle conflicts on commit Recommendation for future: Pessimistic locking for CAD files (merge is impractical). --- ## 6. Web Interface ### 6.1 Features - **Browse**: Navigate item hierarchy (project → assembly → subassembly → part) - **Search**: Full-text search across part numbers, descriptions, properties - **View**: Item details, revision history, relationships, location - **BOM Viewer**: Expandable tree view of assembly structure - **"Open in FreeCAD"**: Launch FreeCAD with specific item via URI handler ### 6.2 URI Handler Register `silo://` protocol handler: ``` silo://open/PROTO-AS-0001 # Open latest revision silo://open/PROTO-AS-0001?rev=3 # Open specific revision ``` ### 6.3 Technology - **Backend**: Go with standard library HTTP - **Frontend**: htmx for interactivity, minimal JavaScript - **Templates**: Go html/template - **Search**: PostgreSQL full-text search (pg_trgm for fuzzy matching) --- ## 7. Revision Tracking ### 7.1 Append-Only Model Every property change creates a new revision record. The current state is always the latest revision, but any historical state can be reconstructed. ``` Item: PROTO-AS-0001 Revision 1 (2026-01-15): Initial creation - description: "Main chassis assembly" - material: null - weight: null Revision 2 (2026-01-20): Updated properties - description: "Main chassis assembly" - material: "6061-T6 Aluminum" - weight: 2.5 Revision 3 (2026-02-01): Design change - description: "Main chassis assembly v2" - material: "6061-T6 Aluminum" - weight: 2.3 ``` ### 7.2 Revision Creation Revisions are created explicitly by user action (not automatic): - `silo commit` from FreeCAD - "Save Revision" button in web UI - API call with explicit revision flag ### 7.3 Revision vs. File Version - **Revision**: Silo metadata revision (tracked in PostgreSQL) - **File Version**: MinIO object version (automatic on upload) A single Silo revision may span multiple file uploads during editing. Only committed revisions create formal revision records. --- ## 8. Relationships and BOM ### 8.1 Relationship Types | Type | Description | Use Case | |------|-------------|----------| | `component` | Part is used in assembly | Standard BOM entry | | `alternate` | Interchangeable substitute | Alternative sourcing | | `reference` | Related item, not in BOM | Drawings, specs, tools | ### 8.2 Reference Designators For assemblies that require them (electronics, complex mechanisms): ```yaml # Relationship record parent: PROTO-AS-0001 child: PROTO-PT-0042 type: component quantity: 4 reference_designators: ["R1", "R2", "R3", "R4"] ``` ### 8.3 Revision-Specific Relationships Relationships can link to specific child revisions or track latest: ```yaml # Locked to specific revision child: PROTO-PT-0042 child_revision: 3 # Always use latest (default for R&D) child: PROTO-PT-0042 child_revision: null # means "latest" ``` Assembly metadata YAML controls default behavior per assembly. --- ## 9. Physical Inventory ### 9.1 Location Management Locations are hierarchical, defined by YAML schema. Each item can exist at multiple locations with quantities. ``` Location: lab/main-area/shelf-a/bin-3 - PROTO-PT-0001: 15 units - PROTO-PT-0002: 8 units Location: lab/storage/shelf-b/bin-1 - PROTO-PT-0001: 50 units (spare stock) ``` ### 9.2 Inventory Operations - **Add**: Increase quantity at location - **Remove**: Decrease quantity at location - **Move**: Transfer between locations - **Adjust**: Set absolute quantity (for cycle counts) All operations logged for audit trail (future consideration). --- ## 10. Authentication (Future) ### 10.1 Current State (MVP) Single-user, no authentication required. ### 10.2 Future: LDAPS Integration Plan for FreeIPA integration: ```yaml # /etc/silo/auth.yaml auth: provider: ldap server: ldaps://ipa.kindred.internal base_dn: "dc=kindred,dc=internal" user_dn_template: "uid={username},cn=users,cn=accounts,dc=kindred,dc=internal" group_base: "cn=groups,cn=accounts,dc=kindred,dc=internal" # Role mapping roles: admin: groups: ["silo-admins"] editor: groups: ["silo-users", "engineers"] viewer: groups: ["silo-viewers"] ``` --- ## 11. API Design ### 11.1 REST Endpoints (Implemented) ``` # Health GET /health # Basic health check GET /ready # Readiness (DB + MinIO) # Web UI GET / # Items page GET /schemas # Schemas page # Schemas GET /api/schemas # List all schemas GET /api/schemas/{name} # Get schema details GET /api/schemas/{name}/properties # Get property schema for category POST /api/schemas/{name}/segments/{segment}/values # Add enum value PUT /api/schemas/{name}/segments/{segment}/values/{code} # Update enum value DELETE /api/schemas/{name}/segments/{segment}/values/{code} # Delete enum value # Projects GET /api/projects # List projects POST /api/projects # Create project GET /api/projects/{code} # Get project PUT /api/projects/{code} # Update project DELETE /api/projects/{code} # Delete project GET /api/projects/{code}/items # Get project items # Items GET /api/items # List/search items POST /api/items # Create item GET /api/items/export.csv # Export items to CSV POST /api/items/import # Import items from CSV GET /api/items/template.csv # Get CSV import template GET /api/items/{partNumber} # Get item details PUT /api/items/{partNumber} # Update item DELETE /api/items/{partNumber} # Archive item # Item-Project Tags GET /api/items/{partNumber}/projects # Get item's projects POST /api/items/{partNumber}/projects # Add project tags DELETE /api/items/{partNumber}/projects/{code} # Remove project tag # Revisions GET /api/items/{partNumber}/revisions # List revisions POST /api/items/{partNumber}/revisions # Create revision GET /api/items/{partNumber}/revisions/compare # Compare two revisions GET /api/items/{partNumber}/revisions/{revision} # Get specific revision PATCH /api/items/{partNumber}/revisions/{revision} # Update status/labels POST /api/items/{partNumber}/revisions/{revision}/rollback # Rollback to revision # Files POST /api/items/{partNumber}/file # Upload file GET /api/items/{partNumber}/file # Download latest file GET /api/items/{partNumber}/file/{revision} # Download file at revision # Part Number Generation POST /api/generate-part-number # Generate without creating item ``` ### 11.2 Not Yet Implemented The following endpoints from the original design are not yet implemented: ``` # Locations (tables exist, no API) GET /api/locations POST /api/locations GET /api/locations/{path} # Inventory (tables exist, no API) GET /api/inventory/{partNumber} POST /api/inventory/{partNumber}/adjust ``` --- ## 12. MVP Scope ### 12.1 Implemented - [x] PostgreSQL database schema (7 migrations) - [x] YAML schema parser for part numbering - [x] Part number generation engine - [x] CLI tool (`cmd/silo`) - [x] API server (`cmd/silod`) with 35+ endpoints - [x] FreeCAD workbench (save, commit, pull, push, info, register, open, browse) - [x] MinIO integration for file storage with versioning - [x] BOM relationships (component, alternate, reference) - [x] Reference designator tracking - [x] Revision history (append-only) with rollback and comparison - [x] Revision status and labels - [x] Project management with many-to-many item tagging - [x] CSV import/export with dry-run validation - [x] Web UI for items and schemas (htmx) - [x] Property schema versioning framework - [x] Docker Compose deployment (dev and prod) - [x] systemd service and deployment scripts ### 12.2 Partially Implemented - [ ] Location hierarchy (database tables exist, no API endpoints) - [ ] Inventory tracking (database tables exist, no API endpoints) - [ ] Date segment type (schema parser placeholder only) - [ ] Part number format validation on creation ### 12.3 Not Started - [ ] Unit tests - [ ] Schema migration tooling - [ ] Multi-user authentication (FreeIPA/LDAP planned) - [ ] Checkout locking - [ ] Approval workflows - [ ] External system integrations (ERP, purchasing) - [ ] Exploded file storage with diffing - [ ] Audit logging - [ ] Notifications - [ ] Reporting/analytics --- ## 13. Open Questions 1. ~~**CLI language**: Go for consistency with web UI, or Python for FreeCAD ecosystem alignment?~~ **Resolved:** Go was chosen for both CLI and API server. 2. **Property schema**: Should item properties be schema-defined (like part numbers) or freeform? Recommendation: Support both—schema defines expected properties, but allow ad-hoc additions. 3. **Thumbnail generation**: Generate thumbnails from .FCStd on commit? Useful for web UI browsing. 4. **Search indexing**: PostgreSQL full-text search sufficient, or add dedicated search (Meilisearch, etc.)? 5. **Offline operation**: Should FreeCAD workbench support offline mode with sync? Adds significant complexity. --- ## 14. References ### 14.1 Design Influences - **CycloneDX BOM specification**: JSON/YAML schema patterns for component identification, relationships, and metadata (https://cyclonedx.org) - **OpenBOM data model**: Reference-instance separation, flexible property schemas - **FreeCAD DynamicData workbench**: Custom property patterns in FreeCAD - **Ansible inventory YAML**: Hierarchical configuration patterns with variable inheritance ### 14.2 Related Standards - **ISO 10303 (STEP)**: Product data representation - **IPC-2581**: Electronics assembly BOM format - **Package URL (PURL)**: Standardized component identification --- ## Appendix A: Example YAML Files ### A.1 Complete Part Numbering Schema See `schemas/kindred-rd.yaml` for the full schema (v3). Summary: ```yaml # kindred-rd-schema.yaml (abbreviated) schema: name: kindred-rd version: 3 description: "Kindred Systems R&D part numbering" separator: "-" uniqueness: scope: global case_sensitive: false segments: - name: category type: enum description: "Category code" required: true values: F01: "Hex Cap Screw" F02: "Socket Head Cap Screw" # ... 70+ categories (see full file) - name: sequence type: serial length: 4 padding: "0" start: 1 description: "Sequential number within category" scope: "{category}" format: "{category}-{sequence}" # Example outputs: F01-0001, R27-0001, A01-0001 ``` ### A.2 Complete Location Schema ```yaml # kindred-locations.yaml location_schema: name: kindred-lab version: 1 description: "Kindred Systems lab and storage locations" path_separator: "/" hierarchy: - level: 0 type: facility description: "Building or site" name_pattern: "^[a-z][a-z0-9-]*$" examples: ["lab", "warehouse", "office"] - level: 1 type: area description: "Room or zone within facility" name_pattern: "^[a-z][a-z0-9-]*$" examples: ["main-lab", "storage", "assembly"] - level: 2 type: shelf description: "Shelving unit" name_pattern: "^shelf-[a-z]$" examples: ["shelf-a", "shelf-b"] - level: 3 type: bin description: "Individual container or bin" name_pattern: "^bin-[0-9]{1,3}$" examples: ["bin-1", "bin-42", "bin-100"] # Properties tracked per location type properties: facility: - name: address type: text required: false area: - name: climate_controlled type: boolean default: false shelf: - name: max_weight_kg type: number required: false bin: - name: bin_size type: enum values: [small, medium, large] default: medium ``` ### A.3 Assembly Configuration ```yaml # Stored as item property or linked document # Example: assembly PROTO-AS-0001 assembly_config: name: "Main Chassis Assembly" relationship_types: - component - alternate - reference use_reference_designators: false child_revision_tracking: latest # Assembly-specific BOM properties relationship_properties: - name: installation_notes type: text - name: torque_spec type: text - name: adhesive_required type: boolean default: false # Validation rules validation: require_quantity: true min_components: 1 ```