Add section 8.4 to SPECIFICATION.md describing the flat BOM flattening
and assembly cost roll-up endpoints with example request/response JSON.
- GET /api/items/{pn}/bom/flat — consolidated leaf parts with
rolled-up quantities and cycle detection
- GET /api/items/{pn}/bom/cost — per-line extended costs and total
assembly cost using standard_cost
Update endpoint count from 74 to 76 in SPECIFICATION.md and README.md.
Add checklist entries for flat BOM and assembly costing features.
927 lines
32 KiB
Markdown
927 lines
32 KiB
Markdown
# Silo: Item Database and Part Management System
|
|
|
|
**Version:** 0.2
|
|
**Date:** February 2026
|
|
**Author:** Kindred Systems LLC
|
|
|
|
---
|
|
|
|
## 1. Overview
|
|
|
|
Silo is an item database with configurable part number generation, designed for R&D-oriented workflows. It provides revision tracking, BOM management, file versioning, and physical inventory location management through a REST API and web UI. CAD integration (FreeCAD workbench, LibreOffice Calc extension) is maintained in separate repositories ([silo-mod](https://git.kindred-systems.com/kindred/silo-mod), [silo-calc](https://git.kindred-systems.com/kindred/silo-calc)).
|
|
|
|
### 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
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ CAD Clients (silo-mod, silo-calc) │
|
|
│ FreeCAD Workbench · LibreOffice Calc Extension │
|
|
│ (maintained in separate repositories) │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│ REST API
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Silo Server (silod) │
|
|
│ - REST API (76 endpoints) │
|
|
│ - Authentication (local, LDAP, OIDC) │
|
|
│ - Schema parsing and validation │
|
|
│ - Part number generation engine │
|
|
│ - Revision management │
|
|
│ - Relationship graph / BOM │
|
|
│ - Web UI (htmx) │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
┌───────────────┴───────────────┐
|
|
▼ ▼
|
|
┌─────────────────────────┐ ┌─────────────────────────────┐
|
|
│ PostgreSQL │ │ MinIO │
|
|
│ (psql.kindred.internal)│ │ - File storage │
|
|
│ - Item metadata │ │ - Versioned objects │
|
|
│ - Relationships │ │ - Thumbnails │
|
|
│ - Revision history │ │ │
|
|
│ - Auth / Sessions │ │ │
|
|
│ - Audit log │ │ │
|
|
└─────────────────────────┘ └─────────────────────────────┘
|
|
```
|
|
|
|
### 2.2 Technology Stack
|
|
|
|
| Component | Technology | Notes |
|
|
|-----------|------------|-------|
|
|
| Database | PostgreSQL 16 | Existing instance at psql.kindred.internal |
|
|
| File Storage | MinIO | S3-compatible, versioning enabled |
|
|
| CLI & API Server | Go (1.24) | chi/v5 router, pgx/v5 driver, zerolog |
|
|
| Authentication | Multi-backend | Local (bcrypt), LDAP/FreeIPA, OIDC/Keycloak |
|
|
| Sessions | PostgreSQL pgxstore | alexedwards/scs, 24h lifetime |
|
|
| 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. Client Integration
|
|
|
|
CAD workbench and spreadsheet extension implementations are maintained in separate repositories ([silo-mod](https://git.kindred-systems.com/kindred/silo-mod), [silo-calc](https://git.kindred-systems.com/kindred/silo-calc)). The Silo server provides the REST API endpoints consumed by those clients.
|
|
|
|
### 5.1 File Storage Strategy
|
|
|
|
Files are stored as whole objects in MinIO with versioning enabled. Storage path convention: `items/{partNumber}/rev{N}.ext`. SHA-256 checksums are captured on upload for integrity verification.
|
|
|
|
Future option: exploded storage (unpack ZIP-based CAD archives for better diffing).
|
|
|
|
### 5.2 Checkout Locking (Future)
|
|
|
|
Future multi-user support will need a server-side locking strategy:
|
|
|
|
- **Pessimistic locking**: Checkout acquires exclusive lock
|
|
- **Optimistic locking**: Allow concurrent edits, handle conflicts on commit
|
|
|
|
Recommendation: 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.
|
|
|
|
### 8.4 Flat BOM and Assembly Costing
|
|
|
|
Two endpoints provide procurement- and manufacturing-oriented views of the BOM:
|
|
|
|
**Flat BOM** (`GET /api/items/{partNumber}/bom/flat`) walks the full assembly tree and returns a consolidated list of **leaf parts only** (parts with no BOM children). Quantities are multiplied through each nesting level and duplicate parts are summed.
|
|
|
|
```
|
|
Assembly A (qty 1)
|
|
├── Sub-assembly B (qty 2)
|
|
│ ├── Part X (qty 3) → total 6
|
|
│ └── Part Y (qty 1) → total 2
|
|
└── Part Z (qty 4) → total 4
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"part_number": "A",
|
|
"flat_bom": [
|
|
{ "part_number": "X", "description": "...", "total_quantity": 6 },
|
|
{ "part_number": "Y", "description": "...", "total_quantity": 2 },
|
|
{ "part_number": "Z", "description": "...", "total_quantity": 4 }
|
|
]
|
|
}
|
|
```
|
|
|
|
**Assembly Cost** (`GET /api/items/{partNumber}/bom/cost`) builds on the flat BOM and multiplies each leaf's `total_quantity` by its `standard_cost` to produce per-line extended costs and a total assembly cost.
|
|
|
|
```json
|
|
{
|
|
"part_number": "A",
|
|
"total_cost": 124.50,
|
|
"cost_breakdown": [
|
|
{ "part_number": "X", "total_quantity": 6, "unit_cost": 10.00, "extended_cost": 60.00 },
|
|
{ "part_number": "Y", "total_quantity": 2, "unit_cost": 7.25, "extended_cost": 14.50 },
|
|
{ "part_number": "Z", "total_quantity": 4, "unit_cost": 12.50, "extended_cost": 50.00 }
|
|
]
|
|
}
|
|
```
|
|
|
|
Both endpoints detect BOM cycles and return **HTTP 409** with the offending path:
|
|
|
|
```json
|
|
{ "error": "cycle_detected", "detail": "BOM cycle detected: A → B → A" }
|
|
```
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
Silo supports three authentication backends that can be enabled independently or combined. When authentication is disabled (`auth.enabled: false`), all routes are open and a synthetic dev user with the `admin` role is injected into every request.
|
|
|
|
### 10.1 Backends
|
|
|
|
| Backend | Use Case | Config Key |
|
|
|---------|----------|------------|
|
|
| **Local** | Username/password stored in database (bcrypt cost 12) | `auth.local` |
|
|
| **LDAP** | FreeIPA / Active Directory via LDAP bind | `auth.ldap` |
|
|
| **OIDC** | Keycloak or any OpenID Connect provider (redirect flow) | `auth.oidc` |
|
|
|
|
### 10.2 Role Model
|
|
|
|
Three roles with a strict hierarchy: `admin > editor > viewer`
|
|
|
|
| Permission | viewer | editor | admin |
|
|
|-----------|--------|--------|-------|
|
|
| Read items, projects, schemas, BOMs | Yes | Yes | Yes |
|
|
| Create/update items and revisions | No | Yes | Yes |
|
|
| Upload files, manage BOMs | No | Yes | Yes |
|
|
| Import CSV/ODS | No | Yes | Yes |
|
|
| Manage own API tokens | Yes | Yes | Yes |
|
|
| User management (future) | No | No | Yes |
|
|
|
|
### 10.3 API Tokens
|
|
|
|
Raw token format: `silo_` + 64 hex characters (32 random bytes from `crypto/rand`). Only the SHA-256 hash is stored in the database. Tokens inherit the owning user's role.
|
|
|
|
### 10.4 Sessions
|
|
|
|
PostgreSQL-backed sessions via `alexedwards/scs` pgxstore. Cookie: `silo_session`, HttpOnly, SameSite=Lax, 24h lifetime. `Secure` flag is set when `auth.enabled` is true.
|
|
|
|
See [AUTH.md](AUTH.md) for full architecture details and [AUTH_USER_GUIDE.md](AUTH_USER_GUIDE.md) for setup instructions.
|
|
|
|
---
|
|
|
|
## 11. API Design
|
|
|
|
### 11.1 REST Endpoints (Implemented)
|
|
|
|
```
|
|
# Health (no auth)
|
|
GET /health # Basic health check
|
|
GET /ready # Readiness (DB + MinIO)
|
|
|
|
# Auth (no auth required)
|
|
GET /login # Login page
|
|
POST /login # Login form handler
|
|
POST /logout # Logout
|
|
GET /auth/oidc # OIDC login redirect
|
|
GET /auth/callback # OIDC callback
|
|
|
|
# Web UI (auth + CSRF)
|
|
GET / # Items page
|
|
GET /projects # Projects page
|
|
GET /schemas # Schemas page
|
|
GET /audit # Audit/completeness page
|
|
GET /settings # User settings / token management
|
|
POST /settings/tokens # Create API token (web)
|
|
POST /settings/tokens/{id}/revoke # Revoke API token (web)
|
|
|
|
# Auth API
|
|
GET /api/auth/me # Current authenticated user
|
|
GET /api/auth/tokens # List user's API tokens
|
|
POST /api/auth/tokens # Create API token
|
|
DELETE /api/auth/tokens/{id} # Revoke API token
|
|
|
|
# Schemas (read: viewer, write: editor)
|
|
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 [editor]
|
|
PUT /api/schemas/{name}/segments/{segment}/values/{code} # Update enum value [editor]
|
|
DELETE /api/schemas/{name}/segments/{segment}/values/{code} # Delete enum value [editor]
|
|
|
|
# Projects (read: viewer, write: editor)
|
|
GET /api/projects # List projects
|
|
GET /api/projects/{code} # Get project
|
|
GET /api/projects/{code}/items # Get project items
|
|
GET /api/projects/{code}/sheet.ods # Export project sheet as ODS
|
|
POST /api/projects # Create project [editor]
|
|
PUT /api/projects/{code} # Update project [editor]
|
|
DELETE /api/projects/{code} # Delete project [editor]
|
|
|
|
# Items (read: viewer, write: editor)
|
|
GET /api/items # List/filter items
|
|
GET /api/items/search # Fuzzy search
|
|
GET /api/items/export.csv # Export items to CSV
|
|
GET /api/items/template.csv # CSV import template
|
|
GET /api/items/export.ods # Export items to ODS
|
|
GET /api/items/template.ods # ODS import template
|
|
POST /api/items # Create item [editor]
|
|
POST /api/items/import # Import items from CSV [editor]
|
|
POST /api/items/import.ods # Import items from ODS [editor]
|
|
|
|
# Item Detail
|
|
GET /api/items/{partNumber} # Get item details
|
|
PUT /api/items/{partNumber} # Update item [editor]
|
|
DELETE /api/items/{partNumber} # Archive item [editor]
|
|
|
|
# Item-Project Tags
|
|
GET /api/items/{partNumber}/projects # Get item's projects
|
|
POST /api/items/{partNumber}/projects # Add project tags [editor]
|
|
DELETE /api/items/{partNumber}/projects/{code} # Remove project tag [editor]
|
|
|
|
# Revisions
|
|
GET /api/items/{partNumber}/revisions # List revisions
|
|
GET /api/items/{partNumber}/revisions/compare # Compare two revisions
|
|
GET /api/items/{partNumber}/revisions/{revision} # Get specific revision
|
|
POST /api/items/{partNumber}/revisions # Create revision [editor]
|
|
PATCH /api/items/{partNumber}/revisions/{revision} # Update status/labels [editor]
|
|
POST /api/items/{partNumber}/revisions/{revision}/rollback # Rollback to revision [editor]
|
|
|
|
# Files
|
|
GET /api/items/{partNumber}/file # Download latest file
|
|
GET /api/items/{partNumber}/file/{revision} # Download file at revision
|
|
POST /api/items/{partNumber}/file # Upload file [editor]
|
|
|
|
# BOM
|
|
GET /api/items/{partNumber}/bom # List direct children
|
|
GET /api/items/{partNumber}/bom/expanded # Multi-level BOM (recursive)
|
|
GET /api/items/{partNumber}/bom/flat # Flattened BOM (leaf parts, rolled-up quantities)
|
|
GET /api/items/{partNumber}/bom/cost # Assembly cost roll-up
|
|
GET /api/items/{partNumber}/bom/where-used # Where-used (parent lookup)
|
|
GET /api/items/{partNumber}/bom/export.csv # Export BOM as CSV
|
|
GET /api/items/{partNumber}/bom/export.ods # Export BOM as ODS
|
|
POST /api/items/{partNumber}/bom # Add BOM entry [editor]
|
|
POST /api/items/{partNumber}/bom/import # Import BOM from CSV [editor]
|
|
PUT /api/items/{partNumber}/bom/{childPartNumber} # Update BOM entry [editor]
|
|
DELETE /api/items/{partNumber}/bom/{childPartNumber} # Remove BOM entry [editor]
|
|
|
|
# Audit (viewer)
|
|
GET /api/audit/completeness # Item completeness scores
|
|
GET /api/audit/completeness/{partNumber} # Item detail breakdown
|
|
|
|
# Integrations — Odoo (read: viewer, write: editor)
|
|
GET /api/integrations/odoo/config # Get Odoo configuration
|
|
GET /api/integrations/odoo/sync-log # Get sync history
|
|
PUT /api/integrations/odoo/config # Update Odoo config [editor]
|
|
POST /api/integrations/odoo/test-connection # Test connection [editor] (stub)
|
|
POST /api/integrations/odoo/sync/push/{partNumber} # Push to Odoo [editor] (stub)
|
|
POST /api/integrations/odoo/sync/pull/{odooId} # Pull from Odoo [editor] (stub)
|
|
|
|
# Sheets (editor)
|
|
POST /api/sheets/diff # Diff ODS sheet against DB [editor]
|
|
|
|
# Part Number Generation (editor)
|
|
POST /api/generate-part-number # Generate without creating item [editor]
|
|
```
|
|
|
|
### 11.2 Not Yet Implemented
|
|
|
|
The following endpoints from the original design are not yet implemented:
|
|
|
|
```
|
|
# Locations (tables exist, no API handlers)
|
|
GET /api/locations
|
|
POST /api/locations
|
|
GET /api/locations/{path}
|
|
DELETE /api/locations/{path}
|
|
|
|
# Inventory (tables exist, no API handlers)
|
|
GET /api/inventory/{partNumber}
|
|
POST /api/inventory/{partNumber}/adjust
|
|
POST /api/inventory/{partNumber}/move
|
|
```
|
|
|
|
---
|
|
|
|
## 12. MVP Scope
|
|
|
|
### 12.1 Implemented
|
|
|
|
- [x] PostgreSQL database schema (10 migrations)
|
|
- [x] YAML schema parser for part numbering
|
|
- [x] Part number generation engine
|
|
- [x] CLI tool (`cmd/silo`)
|
|
- [x] API server (`cmd/silod`) with 76 endpoints
|
|
- [x] MinIO integration for file storage with versioning
|
|
- [x] BOM relationships (component, alternate, reference)
|
|
- [x] Multi-level BOM (recursive expansion with configurable depth)
|
|
- [x] Where-used queries (reverse parent lookup)
|
|
- [x] Flat BOM flattening with quantity roll-up and cycle detection
|
|
- [x] Assembly cost roll-up using standard_cost
|
|
- [x] BOM CSV and ODS export/import
|
|
- [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] ODS spreadsheet import/export (items, BOMs, project sheets)
|
|
- [x] Web UI for items, projects, schemas, audit (htmx)
|
|
- [x] Authentication (local, LDAP, OIDC) with role-based access control
|
|
- [x] API token management (SHA-256 hashed)
|
|
- [x] Session management (PostgreSQL-backed)
|
|
- [x] Audit logging and completeness scoring
|
|
- [x] CSRF protection (nosurf)
|
|
- [x] Fuzzy search
|
|
- [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
|
|
- [ ] Odoo ERP integration (config and sync-log functional; push/pull are stubs)
|
|
|
|
### 12.3 Not Started
|
|
|
|
- [ ] Unit tests (Go server — minimal coverage exists)
|
|
- [ ] Schema migration tooling
|
|
- [ ] Checkout locking
|
|
- [ ] Approval workflows
|
|
- [ ] Exploded file storage with diffing
|
|
- [ ] Notifications
|
|
- [ ] Reporting/analytics
|
|
|
|
---
|
|
|
|
## 13. Open Questions
|
|
|
|
1. **Thumbnail generation**: Generate thumbnails from CAD files on commit? Useful for web UI browsing.
|
|
|
|
2. **Search indexing**: PostgreSQL full-text search sufficient, or add dedicated search (Meilisearch, etc.)?
|
|
|
|
3. **Checkout locking**: Pessimistic vs optimistic locking strategy for multi-user CAD file editing.
|
|
|
|
---
|
|
|
|
## 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
|
|
- **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
|
|
```
|