update documentation and specs

This commit is contained in:
Forbes
2026-01-29 13:10:12 -06:00
parent d886a44288
commit f39aef0fc7
10 changed files with 546 additions and 1224 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Kindred Systems LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,10 +1,10 @@
# Silo
# Kindred Silo
Item database and part management system for FreeCAD.
Item database and part management system for Kindred Create.
## Overview
Silo is an R&D-oriented item database with:
Kindred Silo is an R&D-oriented item database with:
- **Configurable part number generation** via YAML schemas
- **FreeCAD integration** with git-like commands (checkout, commit, status)
@@ -20,20 +20,19 @@ silo/
│ ├── silo/ # CLI tool
│ └── silod/ # API server
├── internal/
│ ├── api/ # HTTP handlers, routes, and templates
│ ├── config/ # Configuration loading
│ ├── db/ # PostgreSQL access
│ ├── schema/ # YAML schema parsing
│ ├── storage/ # MinIO file storage
│ ├── migration/ # Property migration utilities
│ ├── partnum/ # Part number generation
│ ├── inventory/ # Location and stock management
│ └── api/ # HTTP handlers
│ ├── schema/ # YAML schema parsing
│ └── storage/ # MinIO file storage
├── pkg/
│ └── freecad/ # FreeCAD workbench (Python)
├── web/
│ ├── templates/ # HTML templates
│ └── static/ # CSS, JS assets
├── migrations/ # Database migrations
├── schemas/ # Example YAML schemas
├── migrations/ # Database migration SQL scripts
├── schemas/ # Part numbering schema definitions (YAML)
├── deployments/ # Docker Compose and systemd configs
├── scripts/ # Deployment and setup scripts
└── docs/ # Documentation
```
@@ -51,26 +50,29 @@ cp config.example.yaml config.yaml
go run ./cmd/silod
# CLI usage
go run ./cmd/silo register --schema kindred-rd --project PROTO --type AS
go run ./cmd/silo register --schema kindred-rd --category F01
```
## Configuration
See `config.example.yaml` for all options.
## FreeCAD Integration
## Kindred Create Integration
Install the workbench:
```bash
ln -s $(pwd)/pkg/freecad ~/.local/share/FreeCAD/Mod/Silo
ln -s $(pwd)/pkg/freecad ~/.local/share/FreeCAD/Mod/KindredSilo
```
Then in FreeCAD:
- `silo checkout PROTO-AS-0001`
- `silo commit -m "Updated dimensions"`
- `silo status`
Then in Kindred Create, use the Silo workbench toolbar commands:
- **Pull** - Download an item by part number
- **Commit** - Save current state as a new revision with comment
- **Push** - Batch upload modified files
- **Info** - View revision history
## License
Proprietary - Kindred Systems LLC
MIT License - Copyright (c) 2026 Kindred Systems LLC
See [LICENSE](LICENSE) for details.

View File

@@ -102,7 +102,7 @@ This document compares Silo's current capabilities against SOLIDWORKS PDM—the
| Feature | Status | Notes |
|---------|--------|-------|
| FreeCAD Workbench | ~60% | Commands defined, upload/commit working, needs testing |
| FreeCAD Workbench | ~80% | 8 commands implemented (save, commit, pull, push, info, register, open, browse), needs end-to-end testing |
| Date segment type | Not started | Schema parser placeholder exists |
| Part number validation | Not started | API accepts but doesn't validate format |
| Location hierarchy CRUD | Schema only | Tables exist, no API endpoints |
@@ -113,10 +113,12 @@ This document compares Silo's current capabilities against SOLIDWORKS PDM—the
| Component | Status |
|-----------|--------|
| PostgreSQL | Running |
| MinIO | Configured (CPU compatibility issue being resolved) |
| PostgreSQL | Running (psql.kindred.internal) |
| MinIO | Configured in Docker Compose |
| Silo API Server | Builds successfully |
| Docker Compose | Complete with all services |
| Docker Compose | Complete (dev and production) |
| systemd service | Unit file and env template ready |
| Deployment scripts | setup-host, deploy, init-db, setup-ipa-nginx |
---
@@ -308,8 +310,11 @@ File storage works well. Thumbnail generation and file preview would significant
| Feature | Description | Status |
|---------|-------------|--------|
| MinIO integration | Resolve CPU compatibility, test upload/download | In Progress |
| FreeCAD workbench | Complete and test checkout/commit/status commands | In Progress |
| MinIO integration | File upload/download with versioning and checksums | Complete |
| FreeCAD workbench | 8 toolbar commands implemented | Needs Testing |
| Revision control | Rollback, comparison, status/labels | Complete |
| CSV import/export | Dry-run validation, template generation | Complete |
| Project management | CRUD, many-to-many item tagging | Complete |
| Unit tests | Core API and database operations | Not Started |
| Date segment type | Support date-based part number segments | Not Started |
| Part number validation | Validate format on creation | Not Started |
@@ -384,19 +389,16 @@ File storage works well. Thumbnail generation and file preview would significant
### Phase 1 Detailed Tasks
#### 1.1 MinIO Integration Completion
- [ ] Verify MinIO container runs on target VM
- [ ] Test file upload via REST API
- [ ] Test file download via REST API
- [ ] Test FreeCAD workbench upload
- [ ] Verify version history in MinIO console
#### 1.1 MinIO Integration -- COMPLETE
- [x] MinIO service configured in Docker Compose
- [x] File upload via REST API
- [x] File download via REST API (latest and by revision)
- [x] SHA256 checksums on upload
#### 1.2 FreeCAD Workbench Completion
- [ ] Test `silo checkout` command
- [ ] Test `silo commit` with file upload
- [ ] Test `silo status` for modification detection
- [ ] Test `silo log` for revision history
- [ ] Test `silo register` for new part creation
#### 1.2 FreeCAD Workbench -- Needs End-to-End Testing
- [x] Silo_Save, Silo_Commit, Silo_Pull, Silo_Push implemented
- [x] Silo_Info, Silo_Register, Silo_Open, Silo_Browse implemented
- [ ] End-to-end testing with running Silo instance
- [ ] Document workbench installation
#### 1.3 Unit Test Suite
@@ -411,7 +413,6 @@ File storage works well. Thumbnail generation and file preview would significant
#### 1.4 Missing Segment Types
- [ ] Implement date segment type
- [ ] Add strftime-style format support
- [ ] Update schema documentation
#### 1.5 Location & Inventory APIs
- [ ] `GET /api/locations` - List locations
@@ -460,8 +461,10 @@ File storage works well. Thumbnail generation and file preview would significant
- [SOLIDWORKS PDM API Getting Started](https://3dswym.3dexperience.3ds.com/wiki/solidworks-news-info/getting-started-with-the-solidworks-pdm-api-solidpractices_gBCYaM75RgORBcpSO1m_Mw)
### Silo Documentation
- [Silo Specification](docs/SPECIFICATION.md)
- [Specification](docs/SPECIFICATION.md)
- [Development Status](docs/STATUS.md)
- [Deployment Guide](docs/DEPLOYMENT.md)
- [Gap Analysis](docs/GAP_ANALYSIS.md)
---
@@ -472,21 +475,25 @@ File storage works well. Thumbnail generation and file preview would significant
| **Version Control** | Check-in/out | Yes | Yes | No | Phase 2 |
| | Version history | Yes | Yes | Yes | - |
| | Rollback | Yes | Yes | Yes | - |
| | Revision labels/status | Yes | Yes | Yes | - |
| | Revision comparison | Yes | Yes | Yes (metadata) | - |
| **Workflow** | Custom workflows | Limited | Yes | No | Phase 3 |
| | Parallel approval | No | Yes | No | Phase 3 |
| | Notifications | No | Yes | No | Phase 3 |
| **Security** | User auth | Windows | Windows/LDAP | No | Phase 2 |
| | Permissions | Basic | Granular | No | Phase 2 |
| | Audit trail | Basic | Full | No | Phase 2 |
| **Search** | Metadata search | Yes | Yes | Partial | Phase 4 |
| **Search** | Metadata search | Yes | Yes | Partial (API) | Phase 4 |
| | Content search | No | Yes | No | Phase 4 |
| | Where-used | Yes | Yes | No | Phase 4 |
| **BOM** | Single-level | Yes | Yes | Yes | - |
| | Multi-level | Yes | Yes | Schema only | Phase 5 |
| | BOM export | Yes | Yes | No | Phase 5 |
| **Integration** | API | Limited | Full | Full REST | - |
| **Data** | CSV import/export | Yes | Yes | Yes | - |
| | Project management | Yes | Yes | Yes | - |
| **Integration** | API | Limited | Full | Full REST (35+) | - |
| | ERP connectors | No | Yes | No | Phase 6 |
| | Web access | No | Yes | Partial | Phase 4 |
| | Web access | No | Yes | Yes (htmx) | Phase 4 |
| **Files** | Versioning | Yes | Yes | Yes | - |
| | Preview | Yes | Yes | No | Phase 6 |
| | Multi-site | No | Yes | No | Not Planned |

View File

@@ -17,21 +17,24 @@ This document analyzes the current state of the Silo project against its specifi
| Document | Coverage | Status |
|----------|----------|--------|
| `README.md` | Quick start, overview | Partial (50%) |
| `docs/SPECIFICATION.md` | Design specification | Comprehensive (90%) |
| `docs/STATUS.md` | Development progress | Current but incomplete |
| `silo-spec.md` | Duplicate of SPECIFICATION.md | Redundant |
| `README.md` | Quick start, overview, component map | Current |
| `docs/SPECIFICATION.md` | Design specification, API reference | Current |
| `docs/STATUS.md` | Implementation status summary | Current |
| `docs/DEPLOYMENT.md` | Production deployment guide | Current |
| `docs/GAP_ANALYSIS.md` | SOLIDWORKS PDM comparison, roadmap | Current |
| `ROADMAP.md` | Feature roadmap and phases | Current |
| `silo-spec.md` | Redirect to `docs/SPECIFICATION.md` | Consolidated |
### 1.2 Documentation Gaps (Priority Order)
#### High Priority
| Gap | Impact | Effort |
|-----|--------|--------|
| **API Reference** | Users cannot integrate programmatically | Medium |
| **Deployment Guide** | Cannot deploy to production | Medium |
| **Database Schema Guide** | Migration troubleshooting difficult | Low |
| **Configuration Reference** | config.yaml options undocumented | Low |
| Gap | Impact | Effort | Status |
|-----|--------|--------|--------|
| **API Reference** | Users cannot integrate programmatically | Medium | Addressed in SPECIFICATION.md Section 11 |
| **Deployment Guide** | Cannot deploy to production | Medium | Complete (`docs/DEPLOYMENT.md`) |
| **Database Schema Guide** | Migration troubleshooting difficult | Low | Open |
| **Configuration Reference** | config.yaml options undocumented | Low | Open |
#### Medium Priority
@@ -52,10 +55,11 @@ This document analyzes the current state of the Silo project against its specifi
### 1.3 Recommended Actions
1. **Consolidate specs**: Remove `silo-spec.md` duplicate
2. **Create `docs/API.md`**: Full REST endpoint documentation with examples
3. **Create `docs/DEPLOYMENT.md`**: Production deployment guide
4. **Expand README.md**: Add configuration reference section
1. ~~**Consolidate specs**: Remove `silo-spec.md` duplicate~~ Done
2. ~~**Create API reference**: Full REST endpoint documentation~~ Addressed in SPECIFICATION.md
3. ~~**Create `docs/DEPLOYMENT.md`**: Production deployment guide~~ Done
4. **Create configuration reference**: Document all `config.yaml` options
5. **Create database schema guide**: Document migrations and troubleshooting
---
@@ -124,21 +128,21 @@ CREATE TABLE revisions (
### 3.1 Critical Gaps
| Gap | Description | Impact |
|-----|-------------|--------|
| **No rollback** | Cannot revert to previous revision | Data recovery difficult |
| **No comparison** | Cannot diff between revisions | Change tracking manual |
| **No locking** | No concurrent edit protection | Multi-user unsafe |
| **No approval workflow** | No release/sign-off process | Quality control gap |
| Gap | Description | Impact | Status |
|-----|-------------|--------|--------|
| ~~**No rollback**~~ | ~~Cannot revert to previous revision~~ | ~~Data recovery difficult~~ | **Implemented** |
| ~~**No comparison**~~ | ~~Cannot diff between revisions~~ | ~~Change tracking manual~~ | **Implemented** |
| **No locking** | No concurrent edit protection | Multi-user unsafe | Open |
| **No approval workflow** | No release/sign-off process | Quality control gap | Open |
### 3.2 Important Gaps
| Gap | Description | Impact |
|-----|-------------|--------|
| **No branching** | Linear history only | No experimental variants |
| **No tagging** | No named milestones | Release tracking manual |
| **No audit log** | Actions not logged separately | Compliance gap |
| **Thumbnail missing** | Schema exists, not populated | No visual preview |
| Gap | Description | Impact | Status |
|-----|-------------|--------|--------|
| **No branching** | Linear history only | No experimental variants | Open |
| ~~**No tagging**~~ | ~~No named milestones~~ | ~~Release tracking manual~~ | **Implemented** (revision labels) |
| **No audit log** | Actions not logged separately | Compliance gap | Open |
| **Thumbnail missing** | Schema exists, not populated | No visual preview | Open |
### 3.3 Nice-to-Have Gaps
@@ -153,66 +157,15 @@ CREATE TABLE revisions (
## 4. Revision Control Roadmap
### Phase 1: Foundation (Recommended First)
### Phase 1: Foundation -- COMPLETE
**Goal:** Enable safe single-user revision management
#### 1.1 Rollback Support
```
Effort: Medium | Priority: High | Risk: Low
```
All Phase 1 items have been implemented:
**Changes Required:**
- Add `POST /api/items/{pn}/rollback/{rev}` endpoint
- Create new revision copying properties/file from target revision
- FreeCAD: Add `Silo_Rollback` command
**Database:** No schema changes needed (creates new revision from old)
#### 1.2 Revision Comparison API
```
Effort: Medium | Priority: High | Risk: Low
```
**Changes Required:**
- Add `GET /api/items/{pn}/revisions/compare?from={rev1}&to={rev2}` endpoint
- Return property diff (added/removed/changed keys)
- Return file metadata diff (size, checksum changes)
**Implementation:**
```go
type RevisionDiff struct {
FromRevision int `json:"from_revision"`
ToRevision int `json:"to_revision"`
Properties PropertyDiff `json:"properties"`
FileChanged bool `json:"file_changed"`
FileSizeDiff int64 `json:"file_size_diff,omitempty"`
}
type PropertyDiff struct {
Added map[string]any `json:"added,omitempty"`
Removed map[string]any `json:"removed,omitempty"`
Changed map[string]PropertyChange `json:"changed,omitempty"`
}
```
#### 1.3 Revision Labels/Status
```
Effort: Low | Priority: Medium | Risk: Low
```
**Database Migration:**
```sql
ALTER TABLE revisions ADD COLUMN status TEXT DEFAULT 'draft';
-- Values: 'draft', 'review', 'released', 'obsolete'
ALTER TABLE revisions ADD COLUMN labels TEXT[] DEFAULT '{}';
-- Arbitrary tags: ['prototype', 'v1.0', 'customer-approved']
```
**API Changes:**
- Add `PATCH /api/items/{pn}/revisions/{rev}` for status/label updates
- Add filtering by status in list endpoint
- **Rollback**: `POST /api/items/{pn}/revisions/{rev}/rollback` - creates new revision from old
- **Revision Comparison**: `GET /api/items/{pn}/revisions/compare?from={rev1}&to={rev2}` - property and file diffs
- **Revision Labels/Status**: `PATCH /api/items/{pn}/revisions/{rev}` - status (draft/review/released/obsolete) and arbitrary labels via migration 007
---
@@ -375,13 +328,13 @@ Effort: Medium | Priority: Low | Risk: Low
## 5. Recommended Implementation Order
### Immediate (Next Sprint)
### Completed
1. **Revision Comparison API** - High value, low risk
2. **Rollback Support** - Critical for data safety
3. **Revision Labels** - Quick win for workflow
1. ~~**Revision Comparison API**~~ - Implemented
2. ~~**Rollback Support**~~ - Implemented
3. ~~**Revision Labels/Status**~~ - Implemented (migration 007)
### Short-term (1-2 Months)
### Next (Short-term)
4. **Pessimistic Locking** - Required before multi-user
5. **Authentication** - Required before production deployment
@@ -438,10 +391,11 @@ These design decisions remain unresolved:
## Appendix A: File Structure for New Features
Revision endpoints, status, and labels are already implemented in the existing handler files. Future features would add:
```
internal/
api/
handlers_revision.go # New revision endpoints
handlers_lock.go # Locking endpoints
handlers_audit.go # Audit log endpoints
auth/
@@ -452,7 +406,6 @@ internal/
audit.go # Audit repository
releases.go # Release repository
migrations/
007_revision_status.sql # Labels and status
008_item_locks.sql # Locking table
009_audit_log.sql # Audit logging
010_releases.sql # Release management
@@ -462,11 +415,11 @@ migrations/
## Appendix B: API Additions Summary
### Phase 1 Endpoints
### Phase 1 Endpoints (Implemented)
```
GET /api/items/{pn}/revisions/compare # Diff two revisions
POST /api/items/{pn}/rollback/{rev} # Create revision from old
PATCH /api/items/{pn}/revisions/{rev} # Update status/labels
GET /api/items/{pn}/revisions/compare # Diff two revisions
POST /api/items/{pn}/revisions/{rev}/rollback # Create revision from old
PATCH /api/items/{pn}/revisions/{rev} # Update status/labels
```
### Phase 2 Endpoints

View File

@@ -75,8 +75,8 @@ Silo treats **part numbering schemas as configuration, not code**. Multiple numb
| Database | PostgreSQL | Existing instance at psql.kindred.internal |
| File Storage | MinIO | S3-compatible, versioning enabled |
| FreeCAD Integration | Python workbench | Macro-style commands |
| CLI | Go or Python | TBD based on complexity |
| Web UI | Go + htmx | Lightweight, minimal JS |
| CLI & API Server | Go (1.23) | chi/v5 router, pgx/v5 driver, zerolog |
| Web UI | Go html/template + htmx | Lightweight, minimal JS |
---
@@ -186,7 +186,7 @@ Schemas define how part numbers are generated. Each schema consists of **segment
# /etc/silo/schemas/kindred-rd.yaml
schema:
name: kindred-rd
version: 1
version: 3
description: "Kindred Systems R&D part numbering"
# Separator between segments (default: "-")
@@ -194,45 +194,47 @@ schema:
# Uniqueness enforcement
uniqueness:
scope: global # or "per-project", "per-type", "per-schema"
scope: global
case_sensitive: false
segments:
- name: project
type: string
length: 5
description: "Project identifier"
validation:
pattern: "^[A-Z0-9]{5}$"
required: true
- name: part_type
- name: category
type: enum
description: "Type of item"
values:
AS: "Assembly"
PT: "Part"
DW: "Drawing"
DC: "Document"
TB: "Tooling/Fixture"
PC: "Purchased Component"
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" # left-pad with zeros
description: "Sequential number"
scope: "{project}-{part_type}" # counter scope (template)
padding: "0"
start: 1
description: "Sequential number within category"
scope: "{category}"
# Format template (optional, defaults to joining segments with separator)
format: "{project}-{part_type}-{sequence}"
format: "{category}-{sequence}"
# Example outputs:
# PROTO-AS-0001 (first assembly in PROTO project)
# PROTO-PT-0001 (first part in PROTO project)
# ALPHA-AS-0001 (first assembly in ALPHA project)
# 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 |
@@ -248,11 +250,8 @@ schema:
The `scope` field in serial segments supports template variables referencing other segments:
```yaml
# Sequence per project
scope: "{project}"
# Sequence per project AND type (recommended for R&D)
scope: "{project}-{part_type}"
# Sequence per category (current kindred-rd schema)
scope: "{category}"
# Global sequence (no scope)
scope: null
@@ -352,19 +351,18 @@ assembly_config:
### 5.1 Workbench Commands
The Silo workbench provides git-like commands accessible via toolbar, menu, and Python console:
The Silo workbench provides toolbar commands in FreeCAD:
| Command | Description |
|---------|-------------|
| `silo init` | Initialize Silo tracking for current document |
| `silo status` | Show tracked/untracked objects, modifications |
| `silo checkout <part_number>` | Load item from Silo into current document |
| `silo commit` | Save current state as new revision |
| `silo log` | Show revision history |
| `silo diff` | Compare current state to last committed revision |
| `silo register` | Generate part number for selected object(s) |
| `silo link` | Create relationship between objects |
| `silo bom` | Generate BOM from current assembly |
| 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
@@ -566,77 +564,118 @@ auth:
---
## 11. API Design (Sketch)
## 11. API Design
### 11.1 REST Endpoints
### 11.1 REST Endpoints (Implemented)
```
# Items
GET /api/items # List/search items
POST /api/items # Create item
GET /api/items/{part_number} # Get item details
PUT /api/items/{part_number} # Update item (creates revision)
DELETE /api/items/{part_number} # Archive item
# Health
GET /health # Basic health check
GET /ready # Readiness (DB + MinIO)
# Revisions
GET /api/items/{part_number}/revisions
GET /api/items/{part_number}/revisions/{rev}
# Relationships
GET /api/items/{part_number}/bom
POST /api/items/{part_number}/relationships
DELETE /api/items/{part_number}/relationships/{id}
# Files
GET /api/items/{part_number}/file
PUT /api/items/{part_number}/file
GET /api/items/{part_number}/file?rev={rev}
# Web UI
GET / # Items page
GET /schemas # Schemas page
# Schemas
GET /api/schemas
POST /api/schemas
GET /api/schemas/{name}
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
# Locations
# 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
GET /api/inventory/{part_number}
POST /api/inventory/{part_number}/adjust
# Part number generation
POST /api/generate-part-number
Body: { "schema": "kindred-rd", "project": "PROTO", "part_type": "AS" }
Response: { "part_number": "PROTO-AS-0001" }
# Inventory (tables exist, no API)
GET /api/inventory/{partNumber}
POST /api/inventory/{partNumber}/adjust
```
---
## 12. MVP Scope
### 12.1 Included
### 12.1 Implemented
- [ ] PostgreSQL database schema
- [ ] YAML schema parser for part numbering
- [ ] Part number generation engine
- [ ] Basic CLI for item CRUD
- [ ] FreeCAD workbench with core commands (checkout, commit, status, register)
- [ ] MinIO integration for file storage
- [ ] Single-level and multi-level BOM support
- [ ] Reference designator tracking
- [ ] Alternate part tracking
- [ ] Revision history (append-only)
- [ ] Location hierarchy (YAML-defined)
- [ ] Basic inventory tracking (quantity at location)
- [ ] Web UI for browsing and search
- [ ] "Open in FreeCAD" URI handler
- [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 Excluded (Future)
### 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 with authentication
- [ ] Multi-user authentication (FreeIPA/LDAP planned)
- [ ] Checkout locking
- [ ] Approval workflows
- [ ] External system integrations (ERP, purchasing)
@@ -649,7 +688,7 @@ POST /api/generate-part-number
## 13. Open Questions
1. **CLI language**: Go for consistency with web UI, or Python for FreeCAD ecosystem alignment?
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.
@@ -682,12 +721,14 @@ POST /api/generate-part-number
### A.1 Complete Part Numbering Schema
See `schemas/kindred-rd.yaml` for the full schema (v3). Summary:
```yaml
# kindred-rd-schema.yaml
# kindred-rd-schema.yaml (abbreviated)
schema:
name: kindred-rd
version: 1
description: "Kindred Systems R&D part numbering for prototype development"
version: 3
description: "Kindred Systems R&D part numbering"
separator: "-"
@@ -696,50 +737,26 @@ schema:
case_sensitive: false
segments:
- name: project
type: string
length: 5
case: upper
description: "5-character project identifier"
validation:
pattern: "^[A-Z0-9]{5}$"
message: "Project code must be exactly 5 alphanumeric characters"
required: true
- name: part_type
- name: category
type: enum
description: "Two-character type code"
description: "Category code"
required: true
values:
AS: "Assembly - multi-part unit"
PT: "Part - single manufactured item"
DW: "Drawing - technical drawing"
DC: "Document - specification, procedure, etc."
TB: "Tooling - jigs, fixtures, molds"
PC: "Purchased - externally sourced component"
EL: "Electrical - wiring, PCB, electronics"
SW: "Software - firmware, configuration"
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 project/type"
scope: "{project}-{part_type}"
description: "Sequential number within category"
scope: "{category}"
format: "{project}-{part_type}-{sequence}"
# Validation rules applied to complete part number
validation:
min_length: 14
max_length: 14
format: "{category}-{sequence}"
# Metadata for UI/documentation
examples:
- "PROTO-AS-0001"
- "ALPHA-PT-0042"
- "BETA1-EL-0003"
# Example outputs: F01-0001, R27-0001, A01-0001
```
### A.2 Complete Location Schema

View File

@@ -1,137 +1,92 @@
# Silo Development Status
**Date:** 2026-01-23
**Last Updated By:** Claude Code Session
**Last Updated:** 2026-01-29
---
## Current State: MinIO File Upload Implementation
## Implementation Status
### Completed Work
### Core Systems
#### 1. Docker Compose - MinIO Service Added
- File: `deployments/docker-compose.yaml`
- Added MinIO service with versioning enabled
- Configured healthcheck and environment variables
- Note: Using `minio/minio:RELEASE.2024-01-16T16-07-38Z` for CPU compatibility
| Component | Status | Notes |
|-----------|--------|-------|
| PostgreSQL schema | Complete | 7 migrations applied |
| YAML schema parser | Complete | Supports enum, serial, constant, string segments |
| Part number generator | Complete | Scoped sequences, category-based format |
| API server (`silod`) | Complete | 35+ REST endpoints via chi/v5 |
| CLI tool (`silo`) | Complete | Item registration and management |
| MinIO file storage | Complete | Upload, download, versioning, checksums |
| Revision control | Complete | Append-only history, rollback, comparison, status/labels |
| Project management | Complete | CRUD, many-to-many item tagging |
| CSV import/export | Complete | Dry-run validation, template generation |
| Web UI | Complete | Items and schemas pages (htmx) |
| Docker Compose | Complete | Dev and production configurations |
| Deployment scripts | Complete | setup-host, deploy, init-db, setup-ipa-nginx |
| systemd service | Complete | Unit file and environment template |
#### 2. API Endpoints - File Upload/Download
- File: `internal/api/handlers.go`
### FreeCAD Workbench
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/items/{partNumber}/file` | POST | Upload file and create revision |
| `/api/items/{partNumber}/file` | GET | Download latest revision file |
| `/api/items/{partNumber}/file/{revision}` | GET | Download specific revision file |
| `/api/items/{partNumber}/revisions` | POST | Create revision without file |
| Command | Status | Notes |
|---------|--------|-------|
| Silo_Save | Implemented | Auto-save + upload to MinIO |
| Silo_Commit | Implemented | Save with revision comment |
| Silo_Pull | Implemented | Download / create items |
| Silo_Push | Implemented | Batch upload modified files |
| Silo_Info | Implemented | View revision history |
| Silo_Register | Implemented | Generate part number for document |
| Silo_Open | Implemented | Open item by part number |
| Silo_Browse | Implemented | Browse items in list dialog |
#### 3. Routes Added
- File: `internal/api/routes.go`
- All new endpoints wired up
Workbench needs end-to-end testing with a running Silo instance.
#### 4. FreeCAD Client Updated
- File: `pkg/freecad/silo_commands.py`
- Added `_upload_file()` method for multipart form upload
- Updated `create_revision()` to optionally upload files
- Updated `Silo_Commit` command to save document and upload to MinIO
### Not Yet Implemented
#### 5. Build Status
- **Go code compiles successfully** - `go build ./...` passes
| Feature | Notes |
|---------|-------|
| Location API endpoints | Database tables exist (`locations`, `inventory`), no REST handlers |
| Inventory API endpoints | Database tables exist, no REST handlers |
| Date segment type | Schema parser placeholder only |
| Part number format validation | API accepts but does not validate format on creation |
| Unit tests | No test coverage |
---
## Where We Left Off
## Infrastructure
### Problem
MinIO container failing to start due to CPU architecture:
```
Fatal glibc error: CPU does not support x86-64-v2
```
### Solution in Progress
- VM being rebooted to newer architecture
- Already configured older MinIO image as fallback
| Service | Host | Status |
|---------|------|--------|
| PostgreSQL | psql.kindred.internal:5432 | Running |
| MinIO | localhost:9000 (API) / :9001 (console) | Configured |
| Silo API | localhost:8080 | Builds successfully |
---
## Next Steps After VM Reboot
## Schema Status
### 1. Start Services
```bash
cd /home/forbes/projects/silo-0062/deployments
sudo docker compose up -d
```
The part numbering schema (`kindred-rd`) is at **version 3** using the `{category}-{sequence}` format (e.g., `F01-0001`). This replaced the earlier `{project}-{type}-{sequence}` format. Projects are now managed as many-to-many tags rather than being embedded in part numbers.
### 2. Verify Services
```bash
# Check all services
sudo docker compose ps
# Check MinIO health
curl http://localhost:9000/minio/health/live
# Check Silo API with storage
curl http://localhost:8080/ready
```
### 3. Test File Upload
```bash
# Create test file
echo "Test content" > /tmp/test.FCStd
# Upload to existing item
curl -X POST \
-F "file=@/tmp/test.FCStd" \
-F "comment=Test upload" \
-F 'properties={"test": true}' \
http://localhost:8080/api/items/3DX15-A01-0002/file
```
### 4. Test File Download
```bash
# Download latest revision
curl http://localhost:8080/api/items/3DX15-A01-0002/file -o downloaded.FCStd
# Download specific revision
curl http://localhost:8080/api/items/3DX15-A01-0002/file/2 -o rev2.FCStd
```
### 5. Test from FreeCAD
1. Open FreeCAD with Silo workbench
2. Open an existing item: `Silo_Open` command
3. Make changes
4. Commit with file upload: `Silo_Commit` command
5. Verify file appears in MinIO console at http://localhost:9001
The schema defines 70+ categories across 10 groups:
- 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
---
## Remaining MVP Tasks
## Database Migrations
| Task | Status | Priority |
|------|--------|----------|
| Start docker-compose with MinIO | Pending | **Next** |
| Test full upload/download flow | Pending | High |
| Implement date segment support | Pending | Medium |
| Implement part number validation | Pending | Medium |
| Add unit tests | Pending | Medium |
---
## File Changes This Session
```
modified: deployments/docker-compose.yaml (added MinIO service)
modified: internal/api/handlers.go (added file handlers)
modified: internal/api/routes.go (added file routes)
modified: pkg/freecad/silo_commands.py (added file upload)
```
---
## MinIO Console Access
Once running:
- **URL:** http://localhost:9001
- **Username:** silominio
- **Password:** silominiosecret
- **Bucket:** silo-files
| Migration | Description |
|-----------|-------------|
| 001_initial.sql | Core schema (items, revisions, relationships, locations, inventory, sequences) |
| 002_sequence_by_name.sql | Sequence naming changes |
| 003_remove_material.sql | Schema cleanup |
| 004_cad_sync_state.sql | CAD synchronization state |
| 005_property_schema_version.sql | Property versioning framework |
| 006_project_tags.sql | Many-to-many project-item relationships |
| 007_revision_status.sql | Revision status and labels |

View File

@@ -1,18 +1,18 @@
"""Silo FreeCAD Workbench - Item database integration."""
"""Kindred Silo Workbench - Item database integration for Kindred Create."""
import os
import FreeCAD
import FreeCADGui
FreeCAD.Console.PrintMessage("Silo InitGui.py loading...\n")
FreeCAD.Console.PrintMessage("Kindred Silo InitGui.py loading...\n")
class SiloWorkbench(FreeCADGui.Workbench):
"""Silo workbench for item database integration."""
"""Kindred Silo workbench for item database integration."""
MenuText = "Silo"
ToolTip = "Item database and part management"
MenuText = "Kindred Silo"
ToolTip = "Item database and part management for Kindred Create"
Icon = ""
def __init__(self):
@@ -54,7 +54,7 @@ class SiloWorkbench(FreeCADGui.Workbench):
def Activated(self):
"""Called when workbench is activated."""
FreeCAD.Console.PrintMessage("Silo workbench activated\n")
FreeCAD.Console.PrintMessage("Kindred Silo workbench activated\n")
FreeCAD.Console.PrintMessage(
" API: SILO_API_URL (default: http://localhost:8080/api)\n"
)
@@ -73,7 +73,7 @@ class SiloWorkbench(FreeCADGui.Workbench):
"""Show keyboard shortcut recommendations dialog on first activation."""
try:
param_group = FreeCAD.ParamGet(
"User parameter:BaseApp/Preferences/Mod/Silo"
"User parameter:BaseApp/Preferences/Mod/KindredSilo"
)
if param_group.GetBool("ShortcutsShown", False):
return
@@ -81,7 +81,7 @@ class SiloWorkbench(FreeCADGui.Workbench):
from PySide import QtGui
msg = """<h3>Welcome to Silo Workbench!</h3>
msg = """<h3>Welcome to Kindred Silo!</h3>
<p>For the best experience, set up these keyboard shortcuts:</p>
<table style="margin: 10px 0;">
<tr><td><b>Ctrl+O</b></td><td> - </td><td>Silo_Open (Search & Open)</td></tr>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
<name>Silo</name>
<description>Item database and part management workbench</description>
<name>Kindred Silo</name>
<description>Item database and part management workbench for Kindred Create</description>
<version>0.1.0</version>
<maintainer email="info@kindredsystems.io">Kindred Systems</maintainer>
<license file="LICENSE">MIT</license>

View File

@@ -1,833 +1 @@
# 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 | Go or Python | TBD based on complexity |
| Web UI | Go + 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: 1
description: "Kindred Systems R&D part numbering"
# Separator between segments (default: "-")
separator: "-"
# Uniqueness enforcement
uniqueness:
scope: global # or "per-project", "per-type", "per-schema"
segments:
- name: project
type: string
length: 5
description: "Project identifier"
validation:
pattern: "^[A-Z0-9]{5}$"
required: true
- name: part_type
type: enum
description: "Type of item"
values:
AS: "Assembly"
PT: "Part"
DW: "Drawing"
DC: "Document"
TB: "Tooling/Fixture"
PC: "Purchased Component"
required: true
- name: sequence
type: serial
length: 4
padding: "0" # left-pad with zeros
description: "Sequential number"
scope: "{project}-{part_type}" # counter scope (template)
# Format template (optional, defaults to joining segments with separator)
format: "{project}-{part_type}-{sequence}"
# Example outputs:
# PROTO-AS-0001 (first assembly in PROTO project)
# PROTO-PT-0001 (first part in PROTO project)
# ALPHA-AS-0001 (first assembly in ALPHA project)
```
### 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 project
scope: "{project}"
# Sequence per project AND type (recommended for R&D)
scope: "{project}-{part_type}"
# 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 git-like commands accessible via toolbar, menu, and Python console:
| Command | Description |
|---------|-------------|
| `silo init` | Initialize Silo tracking for current document |
| `silo status` | Show tracked/untracked objects, modifications |
| `silo checkout <part_number>` | Load item from Silo into current document |
| `silo commit` | Save current state as new revision |
| `silo log` | Show revision history |
| `silo diff` | Compare current state to last committed revision |
| `silo register` | Generate part number for selected object(s) |
| `silo link` | Create relationship between objects |
| `silo bom` | Generate BOM from current assembly |
### 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 (Sketch)
### 11.1 REST Endpoints
```
# Items
GET /api/items # List/search items
POST /api/items # Create item
GET /api/items/{part_number} # Get item details
PUT /api/items/{part_number} # Update item (creates revision)
DELETE /api/items/{part_number} # Archive item
# Revisions
GET /api/items/{part_number}/revisions
GET /api/items/{part_number}/revisions/{rev}
# Relationships
GET /api/items/{part_number}/bom
POST /api/items/{part_number}/relationships
DELETE /api/items/{part_number}/relationships/{id}
# Files
GET /api/items/{part_number}/file
PUT /api/items/{part_number}/file
GET /api/items/{part_number}/file?rev={rev}
# Schemas
GET /api/schemas
POST /api/schemas
GET /api/schemas/{name}
# Locations
GET /api/locations
POST /api/locations
GET /api/locations/{path}
# Inventory
GET /api/inventory/{part_number}
POST /api/inventory/{part_number}/adjust
# Part number generation
POST /api/generate-part-number
Body: { "schema": "kindred-rd", "project": "PROTO", "part_type": "AS" }
Response: { "part_number": "PROTO-AS-0001" }
```
---
## 12. MVP Scope
### 12.1 Included
- [ ] PostgreSQL database schema
- [ ] YAML schema parser for part numbering
- [ ] Part number generation engine
- [ ] Basic CLI for item CRUD
- [ ] FreeCAD workbench with core commands (checkout, commit, status, register)
- [ ] MinIO integration for file storage
- [ ] Single-level and multi-level BOM support
- [ ] Reference designator tracking
- [ ] Alternate part tracking
- [ ] Revision history (append-only)
- [ ] Location hierarchy (YAML-defined)
- [ ] Basic inventory tracking (quantity at location)
- [ ] Web UI for browsing and search
- [ ] "Open in FreeCAD" URI handler
### 12.2 Excluded (Future)
- [ ] Schema migration tooling
- [ ] Multi-user with authentication
- [ ] 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?
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
```yaml
# kindred-rd-schema.yaml
schema:
name: kindred-rd
version: 1
description: "Kindred Systems R&D part numbering for prototype development"
separator: "-"
uniqueness:
scope: global
case_sensitive: false
segments:
- name: project
type: string
length: 5
case: upper
description: "5-character project identifier"
validation:
pattern: "^[A-Z0-9]{5}$"
message: "Project code must be exactly 5 alphanumeric characters"
required: true
- name: part_type
type: enum
description: "Two-character type code"
required: true
values:
AS: "Assembly - multi-part unit"
PT: "Part - single manufactured item"
DW: "Drawing - technical drawing"
DC: "Document - specification, procedure, etc."
TB: "Tooling - jigs, fixtures, molds"
PC: "Purchased - externally sourced component"
EL: "Electrical - wiring, PCB, electronics"
SW: "Software - firmware, configuration"
- name: sequence
type: serial
length: 4
padding: "0"
start: 1
description: "Sequential number within project/type"
scope: "{project}-{part_type}"
format: "{project}-{part_type}-{sequence}"
# Validation rules applied to complete part number
validation:
min_length: 14
max_length: 14
# Metadata for UI/documentation
examples:
- "PROTO-AS-0001"
- "ALPHA-PT-0042"
- "BETA1-EL-0003"
```
### 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
```
This document has moved to [docs/SPECIFICATION.md](docs/SPECIFICATION.md).

199
silo.svg Normal file
View File

@@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="256"
height="256"
viewBox="0 0 67.733333 67.733333"
version="1.1"
id="svg1"
inkscape:version="1.4.3 (fcd0343856, 2026-01-01)"
sodipodi:docname="silo.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="2.2803882"
inkscape:cx="64.243447"
inkscape:cy="110.72676"
inkscape:window-width="1854"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="14.357431 : 46.829376 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="67.733333 : 33.866667 : 1"
inkscape:persp3d-origin="33.866667 : 22.577778 : 1"
id="perspective2" />
<inkscape:path-effect
effect="bspline"
id="path-effect29"
is_visible="true"
lpeversion="1.3"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false"
uniform="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect28"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,3.9200752,0,1 @ F,0,0,1,0,4.1239928,0,1 @ F,0,0,1,0,4.8617729,0,1 @ F,0,0,1,0,4.8211703,0,1 @ F,0,0,1,0,4.5493151,0,1 @ F,0,0,1,0,4.4938558,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect27"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect18"
is_visible="true"
lpeversion="1.3"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false"
uniform="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect17"
is_visible="true"
lpeversion="1.3"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false"
uniform="false" />
<rect
x="420.0712"
y="303.34592"
width="1.3979075"
height="15.376982"
id="rect10" />
<inkscape:path-effect
effect="bspline"
id="path-effect17-1"
is_visible="true"
lpeversion="1.3"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false"
uniform="false" />
</defs>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
transform="scale(0.26458333)"
id="text10"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect10);display:inline;fill:#000000" />
<path
d="M 20.428727,7.2855111 A 11.978197,11.814115 0 0 0 8.4501302,19.09926 H 32.406807 A 11.978197,11.814115 0 0 0 20.428727,7.2855111 Z"
style="stroke-width:0.266414;fill:#cb5633;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="path13" />
<path
d="M 32.451249,56.100619 H 8.4501302 a 12.000372,4.1021233 0 0 0 12.0008178,4.102076 12.000372,4.1021233 0 0 0 12.000301,-4.102076 z"
style="stroke-width:0.299701;fill:#cb5633;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="path9" />
<path
d="M 32.406807,19.09926 H 8.4501302 A 11.978197,11.814115 0 0 0 20.428727,30.913526 11.978197,11.814115 0 0 0 32.406807,19.09926 Z"
style="stroke-width:0.33633;fill:#cb5633;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="path12" />
<path
d="M 32.406807,19.09926 A 11.978197,11.814115 0 0 1 20.428727,30.913526 11.978197,11.814115 0 0 1 8.4501302,19.09926 V 56.100619 A 12.000372,4.1021233 0 0 1 20.450948,51.998544 12.000372,4.1021233 0 0 1 32.451249,56.100619 V 19.09926 Z"
style="stroke-width:0.33633;fill:#cb5633;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="path11" />
<path
d="M 32.451249,56.100619 A 12.000372,4.1021233 0 0 0 20.450948,51.998544 12.000372,4.1021233 0 0 0 8.4501302,56.100619 Z"
style="stroke-width:0.33633;fill:#cb5633;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="path10" />
<path
sodipodi:type="star"
style="fill:#000000;stroke:#f1d789;stroke-width:0.665;stroke-dasharray:none;stroke-opacity:1"
id="path7"
inkscape:flatsided="true"
sodipodi:sides="6"
sodipodi:cx="41.67757"
sodipodi:cy="55.952961"
sodipodi:r1="20.923403"
sodipodi:r2="18.120197"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-1.0471976"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 41.677571,35.029558 18.120198,10.461702 -1e-6,20.923403 -18.120198,10.461701 -18.120198,-10.461702 0,-20.923403 z"
transform="matrix(0.74549523,0,0,0.74549523,16.552636,5.9857271)" />
<path
sodipodi:type="star"
style="fill:#000000;stroke:#f1d789;stroke-width:0.665;stroke-dasharray:none;stroke-opacity:1"
id="path7-3"
inkscape:flatsided="true"
sodipodi:sides="6"
sodipodi:cx="41.67757"
sodipodi:cy="55.952961"
sodipodi:r1="20.923403"
sodipodi:r2="18.120197"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-1.0471976"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 41.677571,35.029558 18.120198,10.461702 -1e-6,20.923403 -18.120198,10.461701 -18.120198,-10.461702 0,-20.923403 z"
transform="matrix(0.74549523,0,0,0.74549523,16.552636,-9.6125699)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB