Standalone binary (cmd/migrate-storage) that downloads all files from
MinIO and writes them to the local filesystem for decommissioning MinIO.
Queries revision files, item file attachments, and item thumbnails from
the database, then downloads each from MinIO preserving the object key
structure as filesystem paths. Supports --dry-run, --verbose, atomic
writes via temp+rename, and idempotent re-runs (skips existing files
with matching size).
Add docs/INSTALL.md with two installation paths:
- Option A: Docker Compose (all-in-one with PostgreSQL, MinIO,
OpenLDAP, and optional nginx)
- Option B: Daemon install (systemd with external services, links to
setup instructions for PostgreSQL, MinIO, FreeIPA, nginx)
Includes LDAP user/group management instructions, verification steps,
and upgrade procedures for both paths.
Update README.md Quick Start to point to INSTALL.md, add to docs table.
Add redirect banner to DEPLOYMENT.md for first-time users.
Add comments to docker-compose.prod.yaml noting unsupported env vars.
- setup-host.sh: add SILO_DB_HOST and SILO_MINIO_HOST env var
overrides, update Go version from 1.23 to 1.24, expand generated
silod.env template with session secret and admin password fields
- deploy.sh: add SILO_DEPLOY_TARGET and SILO_DB_HOST env var
overrides for target host and database host
- setup-ipa-nginx.sh: replace hardcoded hostname with SILO_HOSTNAME
env var (default: silo.example.internal), parameterize SILO_PORT,
use variable substitution in nginx config template
All scripts retain backward-compatible defaults.
Add docker-compose.allinone.yaml with five services:
- PostgreSQL 16 with auto-applied migrations
- MinIO for S3-compatible file storage
- OpenLDAP (bitnami/openldap:2.6) with memberOf overlay and
preconfigured silo-admins/silo-users/silo-viewers groups
- Silo API server built from Dockerfile
- Nginx reverse proxy (optional, via --profile nginx)
Add scripts/setup-docker.sh interactive helper that generates
deployments/.env and deployments/config.docker.yaml with random
credentials. Supports --non-interactive for CI.
Add deployments/ldap/ LDIF init scripts for memberOf overlay and
Silo role groups. Add deployments/nginx/ reverse proxy configs.
- Fix docker-compose.yaml: mount config.dev.yaml instead of nonexistent
configs/config.yaml
- Add deployments/config.dev.yaml with Docker service names and dev
defaults for zero-setup make docker-up
- Expand .env.example with all SILO_* and LDAP_* variables
- Update config.example.yaml hostnames to localhost with Docker comments
- Add deployments/config.docker.yaml to .gitignore (generated file)
- Update migration count from 11 to 13 across all docs (012_bom_source,
013_move_cost_sourcing_to_props)
- Update endpoint count from 75 to 78 across all docs
- Add 3 missing endpoints to SPECIFICATION.md section 11.1:
GET /api/events (SSE), GET /api/items/by-uuid/{uuid},
POST /api/items/{pn}/bom/merge
- Add migrations 012 and 013 to STATUS.md table
- Fix migration 010 description (sourcing_link and standard_cost moved
to revision properties in 013)
Replace all references to internal hostnames (silo.kindred.internal,
psql.kindred.internal, minio.kindred.internal, ipa.kindred.internal,
keycloak.kindred.internal) with example.internal equivalents.
Replace gitea.kindred.internal and git.kindred.internal with the public
git.kindred-systems.com instance. Also fix stale silo-0062 repo name
in setup-host.sh and DEPLOYMENT.md.
- frontend-spec.md: rewrite CreateItemPane spec for dynamic form
rendering from form descriptor, replace CategoryPicker three-column
spec with multi-stage domain/subcategory picker, replace useCategories
hook with useFormDescriptor, update form sections to dynamic field
groups, mark hierarchical categories as implemented, remove
sourcing_link/standard_cost from item-level DB columns, update types
and implementation order
- SPECIFICATION.md: rename /api/schemas/{name}/properties endpoint to
/api/schemas/{name}/form
- Add ui section to kindred-rd.yaml with category_picker (multi-stage),
item_fields, field_groups, category_field_groups, and field_overrides
- Add UIConfig structs to Go schema parser with full YAML/JSON tags
- Add ValidateUI() to validate field references against property schemas
- Add ValuesByDomain() helper to auto-derive subcategory picker stages
- Implement GET /api/schemas/{name}/form endpoint that returns resolved
form descriptor with field metadata, widget hints, and category picker
- Replace GET /api/schemas/{name}/properties route with /form
- Add FormDescriptor TypeScript types
- Create useFormDescriptor hook (replaces useCategories)
- Rewrite CreateItemPane to render all sections dynamically from descriptor
- Update CategoryPicker with multi-stage domain/subcategory selection
- Delete useCategories.ts (superseded by useFormDescriptor)
- Add migration 013 to copy sourcing_link/standard_cost values into
current revision properties JSONB and drop the columns from items table
- Remove SourcingLink/StandardCost from Go Item struct and all DB queries
(items.go, audit_queries.go, projects.go)
- Remove from API request/response structs and handlers
- Update CSV/ODS/BOM export/import to read these from revision properties
- Update audit handlers to score as regular property fields
- Remove from frontend Item type and hardcoded form fields
- MainTab now reads sourcing_link/standard_cost from item.properties
- CreateItemPane/EditItemPane no longer have dedicated fields for these;
they will be rendered as schema-driven property fields
The server's ReadTimeout (15s) was closing SSE connections shortly after
they were established, causing a rapid connect/disconnect loop. The handler
already disabled WriteTimeout but not ReadTimeout.
- Add source badges (assembly=teal, manual=blue) to BOM display rows
- Add info banner when assembly-sourced entries exist
- Change source input from text field to select dropdown
- Add merge response types to types.ts
Closes#47
Add file_count and files_total_size to item API responses, computed
via batch query on item_files table (no migration needed).
- Add BatchGetFileStats() to audit_queries.go (follows BatchCheckBOM pattern)
- Add file stats to ItemResponse, HandleListItems, HandleGetItem, HandleGetItemByUUID
- Add 'Files' column to ItemTable (default visible in vertical mode)
- Add has_files computed field to audit completeness scoring (weight 1 for manufactured)
Generated 2026-01-31, references HTML templates and 8 migrations
that are now outdated. Superseded by STATUS.md and SPECIFICATION.md.
API.md and silo-spec.md were already deleted in earlier commits.
Add BOM merge endpoint for syncing assembly-derived BOM entries from
FreeCAD's silo-mod plugin.
Merge rules:
- Added: entries in request but not in server BOM are auto-created
with source='assembly'
- Quantity changed: existing entries with different quantity are
auto-updated
- Unchanged: same part and quantity are skipped
- Unreferenced: assembly-sourced entries in server BOM but not in
request are flagged as warnings (never auto-deleted)
- Manual entries are silently ignored in unreferenced detection
Also emits SSE 'bom.merged' event on successful merge (#46).
Promote BOM source from metadata JSONB to a dedicated VARCHAR(20)
column with CHECK constraint ('manual' or 'assembly').
- Add migration 012_bom_source.sql (column, data migration, cleanup)
- Add Source field to Relationship and BOMEntry structs
- Update all SQL queries (GetBOM, GetWhereUsed, GetExpandedBOM, Create)
- Update API response/request types with source field
- Update CSV/ODS export to read e.Source instead of metadata
- Update CSV import to set source on relationship directly
- Update frontend types and BOMTab to use top-level source field
Closes#43
Adds a new read-only endpoint to resolve a Silo item UUID to its full
ItemResponse. Used by silo-mod to resolve FreeCAD document SiloUUID
properties to part numbers during BOM sync.
- Reuses existing ItemRepository.GetByID() (items.id is the stable UUID)
- Returns 404 for archived items
- Registered in viewer-accessible route group (no editor role required)
Implements #17, #18, #19, #20, #21
- Add CSS custom properties for density-dependent spacing (--d-* vars)
in theme.css with comfortable (default) and compact modes
- Create useDensity hook with localStorage persistence and DOM attribute sync
- Add FOUC prevention in main.tsx (sync density before first paint)
- Create shared PageFooter component merging stats + pagination
- Refactor AppShell to flex layout with density toggle button (COM/CMP)
- Consolidate inline pagination from ItemsPage/AuditPage into PageFooter
- Delete FooterStats.tsx (replaced by PageFooter)
- Replace all hardcoded padding/font/gap values in ItemTable, AuditTable,
ItemsToolbar, and AuditToolbar with var(--d-*) references
Comfortable mode is already tighter than the previous hardcoded values.
Compact mode reduces further for power-user density.
Add server-sent events at GET /api/events for live mutation
notifications. Add server mode (normal/read-only/degraded) exposed
via /health, /ready, and SSE server.state events.
New files:
- broker.go: SSE event hub with client management, non-blocking
fan-out, ring buffer history for Last-Event-ID replay, heartbeat
- servermode.go: mode state machine with periodic MinIO health
check and SIGUSR1 read-only toggle
- sse_handler.go: HTTP handler using http.Flusher and
ResponseController to disable WriteTimeout for long-lived SSE
- broker_test.go, servermode_test.go: 13 unit tests
Modified:
- handlers.go: Server struct gains broker/serverState fields,
Health/Ready include mode and sse_clients, write handlers
emit item.created/updated/deleted and revision.created events
- routes.go: register GET /api/events, add RequireWritable
middleware to all 8 editor-gated route groups
- middleware.go: RequireWritable returns 503 in read-only mode
- csv.go, ods.go: emit bulk item.created events after import
- storage.go: add Ping() method for health checks
- config.go: add ReadOnly field to ServerConfig
- main.go: create broker/state, start background goroutines,
SIGUSR1 handler, graceful shutdown sequence
Closes#38, closes#39