| name | setup |
| description | Set up and configure Open Hardware Manager (OHM). Use when the user says "set up", "configure", "install", "get started", asks about environment variables, configuration, or wants help troubleshooting installation. Also use when the user has a question answered by the project documentation. |
| version | 1.0.0 |
| metadata | {"generated_by":"scaffold-setup-skill","generated_at":"2026-06-04T00:00:00.000Z"} |
Open Hardware Manager (OHM) Setup
Natural language setup wizard for OHM. Covers environment detection, configuration, dependency installation, infrastructure, developer onboarding, and documentation Q&A.
Project summary: OHM is a flexible, domain-agnostic framework that matches requirements (OKH hardware designs) with capabilities (OKW manufacturing facilities) across domains like manufacturing and cooking.
Arguments
$ARGUMENTS may contain:
--dry-run โ walk through the full setup flow without making any changes
--section <name> โ jump to a specific section: detect, config, deps, infra, workflow, docs, health
--regenerate-docs-index โ rebuild the documentation index from current MkDocs content
Step 1: Environment Detection
Before asking any questions, check what is already in place. Report a status table (done / missing / error).
Existing configuration:
- Check for
.env file in the repo root โ if present, read it and note which params are set; list missing required params
- Check for
frontend/.env (only relevant if user wants the frontend)
Runtime prerequisites:
- Python:
python3 --version (need โฅ 3.12) OR check for uv: uv --version
- Docker:
docker info (must be running) โ required for Options A and B
- Node.js:
node --version (need โฅ 18) โ only if user wants the frontend dev server
uv: uv --version โ required for Option C (local Python dev)
Infrastructure:
- Docker running:
docker info
- If
.env exists and STORAGE_PROVIDER=aws_s3/azure_blob/gcp_storage: confirm cloud credentials are set
After detection: summarize what's done vs. what still needs setup. If .env is fully configured and Docker is running, offer to go straight to the health check (Step 7).
If --dry-run is active, note this at the top: "Dry run mode โ no changes will be made."
Step 2: Setup Path Selection
Ask which setup path the user wants:
"How do you want to run OHM?"
- Option A โ Published Docker image: Fastest. No clone required. Use when you just want to run the API, not develop it. Requires Docker Desktop.
- Option B โ Docker Compose from source: Recommended for most users. Clone โ configure โ
docker compose up. Requires Docker Desktop.
- Option C โ Local Python development (uv): Required for CLI development, running tests, or modifying Python code. Requires
uv.
Save the answer as $SETUP_PATH and use it to gate questions in Steps 3 and 4.
Step 3: Configuration
Collect values for all required (and relevant optional) parameters. Skip any param already present in .env.
Configuration model (0.8.7+). OHM resolves config in two layers: non-secret
defaults (storage provider / account / container, OKW_SOURCE, CORS) are checked
into the repo per environment at config/environments/<ENVIRONMENT>.toml, and your
process environment / .env is layered on top and wins. This skill collects
values into .env, which is the correct place for both secrets and any override.
So you only need to set in .env what differs from the environment's TOML defaults
(and every secret โ AZURE_STORAGE_KEY, API_KEYS, LLM_* โ which never belong in
the checked-in TOML files). ENVIRONMENT selects which TOML loads.
3a: Environment
Ask: "Are you setting this up for local development or production?"
development โ defaults are safe; encryption keys are optional; CORS is open
production โ encryption keys required; CORS must be restricted; API keys must be set
Save as $ENV_MODE. Questions marked [prod only] are skipped for dev.
3b: API Authentication
Always ask โ even in development, this prevents accidental open access.
API_KEYS โ Comma-separated list of API keys (backward-compatible; storage-based auth is available separately)
- Ask: "Do you have an API key, or should I generate one?"
- Generate: run
python3 -c "import secrets; print(secrets.token_urlsafe(32))" and use it
- Provide: paste it in
- Note: treat as secret โ do not echo back to user
- is_secret: true
AUTH_MODE โ default: hybrid (accepts both env-var keys and storage-based keys)
- Optional โ only ask if user wants to change: enum:
env, storage, hybrid
- For most users, leave as
hybrid
3c: Storage Provider
STORAGE_PROVIDER โ Where OHM stores OKH/OKW data files
- Ask: "Which storage backend are you using?"
local (default, recommended for getting started) โ ask for LOCAL_STORAGE_PATH
aws_s3 โ branch to AWS params
azure_blob โ branch to Azure params
gcp_storage โ branch to GCP params
If local:
LOCAL_STORAGE_PATH โ default: ./storage; ask only if user wants a custom path
STORAGE_BUCKET_NAME โ default: ohm-storage; skip for most users
If aws_s3:
AWS_ACCESS_KEY_ID โ required; type: secret; is_secret: true
AWS_SECRET_ACCESS_KEY โ required; type: secret; is_secret: true
AWS_DEFAULT_REGION โ default: us-east-1; ask if different
AWS_S3_BUCKET โ required; bucket name
If azure_blob:
AZURE_STORAGE_ACCOUNT โ required (canonical name used in code)
AZURE_STORAGE_KEY โ required; type: secret; is_secret: true
AZURE_STORAGE_CONTAINER โ required
- Note:
AZURE_STORAGE_ACCOUNT_NAME, AZURE_STORAGE_ACCOUNT_KEY, AZURE_CONTAINER_NAME are legacy aliases โ set both canonical and legacy if the user's workflow requires the legacy names
- Optional:
AZURE_STORAGE_OKH_CONTAINER_NAME (default: okh), AZURE_STORAGE_OKW_CONTAINER_NAME (default: okw)
- Optional:
AZURE_STORAGE_SERVICE_NAME โ storage service URL for OKH/OKW libraries; leave empty unless required by your Azure setup
If gcp_storage:
GCP_PROJECT_ID โ required
GCP_STORAGE_BUCKET โ required
GCP_CREDENTIALS_JSON โ required; service account JSON; type: secret; is_secret: true
GOOGLE_CLOUD_PROJECT_ID โ legacy alias for GCP_PROJECT_ID; set both for backward compatibility
GOOGLE_CLOUD_STORAGE_BUCKET โ legacy alias for GCP_STORAGE_BUCKET; set both for backward compatibility
3d: LLM Integration
LLM_ENABLED โ default: false
- Ask: "Do you want to enable LLM integration (AI-powered OKH manifest generation and enhanced matching)?"
- No โ set
LLM_ENABLED=false, skip all LLM params
- Yes โ branch to LLM params below
If LLM enabled:
LLM_DEFAULT_PROVIDER / LLM_PROVIDER โ which LLM provider to use
- Ask: "Which LLM provider?"
anthropic (default) โ requires ANTHROPIC_API_KEY
openai โ requires OPENAI_API_KEY; optionally OPENAI_ORGANIZATION_ID
google โ requires GOOGLE_AI_API_KEY
azure โ requires AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT; default version: 2024-02-15-preview
local (Ollama) โ requires OLLAMA_BASE_URL (default: http://localhost:11434)
Provider-specific secrets (ask immediately after provider selection):
ANTHROPIC_API_KEY โ is_secret: true
OPENAI_API_KEY โ is_secret: true
GOOGLE_AI_API_KEY โ is_secret: true
AZURE_OPENAI_API_KEY โ is_secret: true
AZURE_OPENAI_ENDPOINT โ url; format: https://<resource>.openai.azure.com/
AZURE_OPENAI_API_VERSION โ default: 2024-02-15-preview; leave as default unless your Azure deployment uses a different version
LLM_DEFAULT_MODEL / LLM_MODEL โ default: claude-3-sonnet-20240229 (for anthropic); leave as default for most users
LLM_QUALITY_LEVEL โ enum: hobby, professional, medical; default: professional
LLM_STRICT_MODE โ boolean; default: false; enables stricter LLM validation
LLM Encryption (required in production if LLM enabled):
- Ask: "Are you setting this up for production?"
- Dev: warn that defaults are used (not secure for production), skip encryption params
- Prod: encryption credentials are required:
LLM_ENCRYPTION_SALT โ required for prod; is_secret: true; warn: must not be a default value
LLM_ENCRYPTION_PASSWORD โ required for prod; is_secret: true; warn: must not be a default value
- Alternatively,
LLM_ENCRYPTION_KEY โ a pre-generated Fernet key (32 url-safe base64 bytes); leave empty if using salt/password approach
- Note: do not add inline comments on the same line as an empty
LLM_ENCRYPTION_KEY= value โ python-dotenv will treat the comment as the value
3e: Optional Features
Present as a menu โ user can skip all:
"Here are optional features you can configure. Which, if any, would you like to enable?"
Federation (multi-node OHM network):
OHM_FEDERATION_ENABLED โ default: false; enable only if you intend to run peer sync
- If enabled:
OHM_FEDERATION_NODE_NAME โ human-readable name; default: "My OHM Node"
OHM_FEDERATION_NODE_ROLE โ default: peer
OHM_FEDERATION_DATA_DIR โ default: /app/storage/federation
OHM_FEDERATION_MANUAL_PEERS โ optional; comma-separated peer URLs
OHM_FEDERATION_MDNS_ENABLED โ boolean; optional (auto-discovery)
OHM_FEDERATION_SYNC_INTERVAL_SEC โ integer; default: 60
OHM_FEDERATION_SYNC_RATE_LIMIT_PER_MIN โ integer; optional; default: 60
- Note: federation vars must be set in container env (via docker-compose), not just exported in the host shell
- Suggest: copy
.env.federation.example values into .env
Advanced API settings (rarely needed):
CORS_ORIGINS โ default: * (dev), empty (prod); ask only in prod if user needs to restrict
API_HOST โ default: 0.0.0.0; only change if binding to a specific interface
API_PORT โ default: 8001; only change if the port conflicts
AUTH_ENABLE_STORAGE โ boolean; default: true; enable storage-based API key management
AUTH_CACHE_TTL โ integer seconds; default: 300; cache TTL for validated keys
AUTH_KEY_LENGTH โ integer; default: 32; length in bytes for generated API keys
LOG_FILE โ path; default: logs/app.log
OHM_SERVER_URL โ url; default: http://localhost:8001; CLI default server URL (set this if your API is on a different port or host)
Cache settings (rarely needed):
CACHE_ENABLED โ boolean; default: true
CACHE_BACKEND โ memory or redis; default: memory (use redis for multi-replica / ACA)
CACHE_REDIS_URL โ Redis protocol URL; required when CACHE_BACKEND=redis (e.g. redis://redis:6379/0)
CACHE_KEY_PREFIX โ string; default: ohm; namespace for all cache keys
CACHE_MAX_SIZE โ integer; default: 1000; max LRU entries (memory backend only)
CACHE_CLEANUP_INTERVAL โ integer seconds; default: 60 (memory backend only)
Rate limiting (rarely needed):
RATE_LIMIT_ENABLED โ boolean; default: true
RATE_LIMIT_CLEANUP_INTERVAL โ integer seconds; default: 60
Matching tuning:
MAX_DEPTH โ BOM explosion depth; default: 5; range 1โ10; only ask if user has performance concerns
Domain toggles:
COOKING_DOMAIN_ENABLED โ default: true
MANUFACTURING_DOMAIN_ENABLED โ default: true
Developer/testing:
DEV_MODE โ default: false
LOG_LEVEL โ default: INFO; enum: DEBUG/INFO/WARNING/ERROR
TEST_DATA_DIR โ default: test-data
Repository URLs (optional, for documentation/examples only):
OKH_LIBRARY_REPO_URL โ url; optional; repository URL for the OKH library
OKF_SCHEMA_REPO_URL โ url; optional; repository URL for the OKF schema
Step 4: Config File Generation
Write collected answers to .env in the repo root.
Format:
# Generated by OHM setup skill on <date>
# Edit this file to change configuration. See env.template for all options.
ENVIRONMENT=<value>
API_KEYS=<value>
STORAGE_PROVIDER=<value>
# ... only params relevant to chosen options
Note on env.template: the block between its BEGIN/END GENERATED markers is
emitted from the config schema โ do not hand-edit it (run make env-template to
regenerate). Everything outside the markers is hand-maintained. .env still
overrides both the generated defaults and the per-environment TOML files.
Rules:
- Only write params relevant to the chosen storage provider and enabled features
- Do not write cloud provider params for providers that are not selected
- Write secret params without quoting and without inline comments (especially for empty LLM_ENCRYPTION_KEY)
- Never overwrite params already present in
.env unless user explicitly confirmed replacement
- Add a comment block before each logical section (storage, LLM, federation)
For Option A (published Docker image), instead of writing a .env file, show the equivalent docker run command with -e flags or --env-file usage.
If --dry-run is active: show the would-be .env contents but do not write the file.
Step 5: Dependency Installation
Run in order. Stop and report if any step fails.
Option A: Published Docker image
docker pull touchthesun/openhardwaremanager:latest
Then run with --env-file .env or explicit -e flags:
docker run -p 8001:8001 --env-file .env touchthesun/openhardwaremanager:latest
Or for local storage with no credentials:
docker run -p 8001:8001 \
-e STORAGE_PROVIDER=local \
-e LLM_ENABLED=false \
touchthesun/openhardwaremanager:latest
Option B: Docker Compose from source
No Python install needed โ Docker builds the image.
cp env.template .env
docker compose up --build ohm-api
To include Prometheus monitoring:
docker compose --profile monitoring up
Option C: uv local development
Prerequisite: uv must be installed (curl -LsSf https://astral.sh/uv/install.sh | sh or brew install uv)
uv sync
uv sync --extra dev
source .venv/bin/activate
ohm --help
Step 6: Infrastructure Setup
Start the API server
Option B (Docker Compose):
docker compose up ohm-api
docker compose up -d ohm-api
docker compose logs -f ohm-api
docker compose up --build ohm-api
docker compose down
Option C (local Python):
uv run uvicorn src.api.main:app --host 0.0.0.0 --port 8001 --reload
docker compose up -d ohm-api
Storage initialization
For local storage: no action needed โ OHM creates the directory structure on first use.
For cloud storage: verify credentials by making a test API call after the server starts:
curl -s http://localhost:8001/health
/health includes a storage fingerprint โ the resolved provider / account /
container the app is actually using, plus okh/ and okw/ object counts. Use it
to confirm you are pointed at the container you expect and that it has data
(counts 0 usually means an empty or mis-pointed container). Note: in
ENVIRONMENT=production the app hard-fails on startup if storage config is
invalid or incomplete (e.g. azure_blob without account/container/key); in
development it warns and continues degraded โ so a failed prod boot is almost
always a missing storage value.
Step 7: Developer Workflow Onboarding
Brief the user on day-to-day development commands.
Running the API
docker compose up -d ohm-api
Using the CLI (Option C only)
ohm system health
ohm okh list-manifests
ohm okh upload path/to/manifest.yaml
ohm okh validate <id>
ohm okw list-files
ohm okw search --query "CNC machining"
ohm match --okh <okh-id> --domain manufacturing
ohm llm providers info
ohm okh generate-from-url https://github.com/example/hardware-project
Running tests
uv run pytest tests -m unit
uv run pytest
uv run pytest --cov=src tests -m unit
Code quality
make lint
make format
make format-check
make check
Common Docker commands
docker compose up -d ohm-api
docker compose logs -f ohm-api
docker compose up --build ohm-api
docker compose down
docker compose --profile monitoring up
Environment files
env.template โ canonical template with all params documented; copy to .env
.env.federation.example โ federation vars; copy into .env to enable federation
config/llm_config.json.example โ advanced LLM provider config; copy to config/llm_config.json for fine-grained control (optional; env vars take precedence for most use cases)
Core Workflows: Matching and generate-from-url
These are the two highest-value features in OHM. Walk users through these after the API is running and data is loaded.
Workflow 1: Generate an OKH manifest from a GitHub URL
generate-from-url uses LLM to analyze a hardware project repo and produce a structured OKH manifest. Requires LLM to be enabled (see Step 3d).
What it does: Fetches the repository, reads README/docs/source, and generates a structured OKH manifest describing the hardware's requirements, processes, and materials.
Via CLI (Option C):
ohm okh generate-from-url https://github.com/<owner>/<repo> --use-llm
ohm okh generate-from-url https://github.com/<owner>/<repo> \
--llm-provider anthropic \
--llm-model claude-sonnet-4-5-20250929
ohm okh generate-from-url https://github.com/<owner>/<repo> \
--use-llm \
--output my_manifest.okh.json
Via REST API:
curl -X POST http://localhost:8001/v1/api/okh/generate-from-url \
-H "Content-Type: application/json" \
-H "X-API-Key: <your-api-key>" \
-d '{"url": "https://github.com/<owner>/<repo>"}'
What to expect:
- The call may take 10โ60 seconds depending on repo size and LLM provider latency
- The response is a full OKH manifest JSON; the manifest is also stored in OHM's storage
- If LLM is disabled, a stub manifest is generated from metadata only (no deep analysis)
If the call fails:
- Check
LLM_ENABLED=true and the relevant API key are set in .env
- Check LLM health:
ohm llm providers info or curl http://localhost:8001/v1/api/llm/health
- Check server logs:
docker compose logs -f ohm-api
Workflow 2: Match an OKH manifest to OKW facilities
Matching connects a hardware design (OKH) to manufacturing facilities (OKW) that can produce it. Requires at least one OKH manifest and at least one OKW facility to be loaded into storage.
Step A: Load data
Option 1 โ Use test data bundled in the repo:
ohm okh upload test-data/okh/example-manifest.json
ohm okw upload test-data/okw/example-facility.json
Option 2 โ Generate an OKH manifest from a URL (Workflow 1 above), then upload a facility file you have:
ohm okw upload /path/to/your/facility.json
Verify data is loaded:
ohm okh list-manifests
ohm okw list-files
Step B: Run a match
ohm match requirements <path-to-okh-manifest.json>
ohm match requirements manifest.json --domain manufacturing
ohm match requirements manifest.json \
--use-llm \
--llm-provider anthropic \
--quality-level professional
ohm match requirements manifest.json \
--facility-id <facility-uuid>
ohm match requirements manifest.json \
--location "San Francisco" \
--min-confidence 0.8
Via REST API:
curl -X POST http://localhost:8001/v1/api/match \
-H "Content-Type: application/json" \
-H "X-API-Key: <your-api-key>" \
-d @manifest.json
What to expect:
- The response includes matched facilities with confidence scores
- Each match entry shows which requirements were satisfied and which were gaps
- Higher
--quality-level values produce more thorough validation but require more LLM tokens
If no matches are returned:
- Confirm OKW facilities are loaded:
ohm okw list-files
- Try without
--domain to let OHM auto-detect
- Lower
--min-confidence if you have a threshold set
- Check domain health:
ohm utility domains
Step C: Save and inspect solutions
ohm match list-recent --limit 10
ohm solution save match-output.json --ttl-days 30
ohm solution list
Step 8: Health Check
Verify the setup is working.
-
API health:
curl -s http://localhost:8001/health
Expected: {"status": "healthy", ...}
-
API docs accessible:
Open http://localhost:8001/v1/docs in a browser โ should show the Swagger UI.
-
Storage check (via CLI, Option C):
ohm system health
-
LLM check (if LLM enabled):
curl -s http://localhost:8001/v1/api/llm/health
ohm llm providers info
-
Run unit tests (Option C):
uv run pytest tests -m unit
Report: "Setup complete. OHM is running at http://localhost:8001." โ or list any errors.
Documentation Q&A
When the user asks a question about OHM configuration, features, or usage, find the answer using this index.
Two corpora (do not mix):
docs-site/docs/ โ user-facing product docs (about, guides, reference)
docs/ โ developer / agent corpus (rationale, ADRs, ops judgment, adapter contracts)
Live API catalog: /v1/docs on a running node. Domain glossary: CONTEXT.md.
Harness / probe ops: harness/README.md.
Developer docs index โ regenerated 2026-07-31
| Section | File | Summary |
|---|
| Home | docs/index.md | Developer corpus map |
| Federation MVP ADR | docs/architecture/federation-mvp-adr.md | Federation decisions and scope |
| Facility claim ladder ADR | docs/architecture/facility-claim-ladder-adr.md | Attribution / claim / vouch model |
| Process Taxonomy ADR | docs/architecture/process-taxonomy-adr.md | Process taxonomy decisions |
| File type taxonomy ADR | docs/architecture/file-type-taxonomy-adr.md | Generation file-type classification |
| Identity Model | docs/architecture/identity-model.md | DIDs, grants, attestations, visibility |
| Security Modes | docs/architecture/security-modes.md | peacetime / crisis / shielded posture |
| Source Platforms | docs/architecture/source-platforms.md | Supported vs roadmap ingest hosts |
| Federation Infra | docs/ops/federation-infra.md | Multi-node bring-up expectations |
| Rule Design | docs/ops/rule-design.md | Heuristic matching rule guidelines |
| Release | docs/RELEASE.md | Cut and publish a release |
| MSF Datasheet | docs/adapters/msf-datasheet.md | OKH โ MSF datasheet mapping contract |
| OKH-LOSH TOML | docs/adapters/okh-losh-toml.md | OKH-LOSH TOML mapping contract |
To answer a documentation question:
- Pick the right corpus (user-facing vs developer)
- Find the most relevant file from the index above or
docs-site/
- Prefer code/OpenAPI when the question is about current behavior; cite docs for rationale
To regenerate this index after documentation changes:
Run this skill with --regenerate-docs-index.
Anti-patterns
- Don't set params the user hasn't provided
- Don't skip the health check โ a broken setup is worse than a slow one
- Don't store secret values in conversation history; handle them write-only
- If a step fails, stop and report the error โ don't continue to the next step
- Don't set LLM_ENCRYPTION_KEY with an inline comment on the same line when the value is empty โ python-dotenv will treat the comment as the value
- Don't enable federation unless the user explicitly wants multi-node peer sync; it adds complexity with no benefit for single-node setups
- For cloud storage, don't write both canonical and legacy Azure env var names unless the user's toolchain requires the legacy names