A self-hosted search engine and documentation platform for a large curated collection
of n8n workflow .json files. At its core it is a Python FastAPI service backed by a
SQLite FTS5 index, with a parallel Node.js (Express) implementation of the same API,
a static single-page front end, and container/Kubernetes deployment assets. This guide
covers architecture, configuration, security, operations, and internals in enough depth to
deploy, harden, extend, or reimplement the system.
Tip
For end-user search syntax, the web UI, and everyday workflow discovery, see the companion User Guide. This document assumes you operate or develop the platform.
Important
Scope note — what is and isn't the product. The shipping, wired-together application is the core search server (
run.py→api_server.py→workflow_db.py) plus its static front end, with a Node.js equivalent undersrc/. The repository also contains several standalone, experimental feature modules (src/ai_assistant.py,src/analytics_engine.py,src/enhanced_api.py,src/integration_hub.py,src/performance_monitor.py,src/user_management.py,src/community_features.py) that each build their own FastAPI app and are not mounted into the main server. They are documented in Section 9 and clearly labeled as optional/standalone. The repository additionally bundles an unrelated subproject,medcards-ai/(a Next.js application), which is not part of this platform and is out of scope here.
1. Executive Summary
| Attribute | Detail |
|---|---|
| Purpose | Index, search, browse, visualize, and serve a large collection of n8n workflow JSON files |
| Primary stack | Python 3.9–3.12, FastAPI 0.109, Uvicorn 0.27, Pydantic 2.5, SQLite (FTS5) |
| Alternative stack | Node.js ≥18, Express, sqlite3, fs-extra (mirrors the core API) |
| Storage | Single SQLite database file (database/workflows.db by default); WAL mode |
| Front end | Static single-page app (vanilla JS) served from static/; a GitHub Pages mirror lives in docs/ |
| API surface (core) | 12 REST endpoints + Swagger UI at /docs |
| Published catalog size | 4,343 workflows, 29,528 nodes, 268 integrations, 16 categories |
| Deployment | Docker / Docker Compose (dev + prod overlays), Kubernetes manifests (k8s/), Helm chart (helm/workflows-docs/) |
| External services required | None at runtime — fully self-contained and offline-capable |
Design goals reflected in the code:
- Speed — SQLite FTS5 + targeted indexes + WAL/PRAGMA tuning for sub-100 ms search.
- Self-contained — no external database, message broker, or cloud dependency.
- Incremental indexing — per-file MD5 hashing skips unchanged files on re-index.
- Defense at the file boundary — strict filename validation guards the only file-serving endpoints against path traversal.
2. Architecture Overview
2.1 System Topology
Browser / API client
│
▼
┌──────────────────────────────────────────────────┐
│ Core Server (choose ONE stack) │
│ │
│ Python: run.py ──► api_server.py (FastAPI) │
│ │ Uvicorn/Gunicorn │
│ ▼ │
│ workflow_db.py │
│ (WorkflowDatabase) │
│ │
│ Node: src/server.js (Express) │
│ │ │
│ ▼ │
│ src/database.js │
└──────────────────────────┬─────────────────────────┘
│
┌──────────────────┼───────────────────┐
▼ ▼ ▼
database/workflows.db workflows/**.json static/ (+ docs/ mirror)
(SQLite, FTS5, WAL) (source of truth) (front end assets)
▲
context/*.json (category maps)
The workflow .json files in workflows/ are the source of truth. The database is a
derived, rebuildable index — deleting it and re-indexing reproduces all state.
2.2 Request flow (core search)
- Client calls
GET /api/workflows?q=...&trigger=.... api_server.pyparses/validates query params via Pydantic-typed FastAPI signatures.- It delegates to
WorkflowDatabase.search_workflows()inworkflow_db.py. - If a query string is present, the DB runs an FTS5
MATCHagainstworkflows_fts, joined to theworkflowstable, ordered by FTSrank; otherwise it runs a filtered table scan ordered byanalyzed_at. - Results are validated into
WorkflowSummaryPydantic models and returned as a paginatedSearchResponse.
2.3 Component inventory
| Component | File | Responsibility |
|---|---|---|
| Launcher | run.py |
Dependency check, directory setup, index bootstrap, start Uvicorn |
| API app | api_server.py |
FastAPI app, routes, CORS, GZip, rate limiting, filename validation |
| Data layer | workflow_db.py |
SQLite schema, indexing, FTS search, stats, categories |
| Node API | src/server.js |
Express server mirroring the core endpoints |
| Node data layer | src/database.js |
WorkflowDatabase (sqlite3) — schema, indexing, FTS search |
| Node indexer | src/index-workflows.js |
CLI to (re)build the database in the Node stack |
| Front end | docs/js/search.js, docs/js/app.js |
Client search, filtering, cards, modal, keyboard shortcuts |
| Category data | context/unique_categories.json, context/search_categories.json |
Category list + filename→category mappings |
3. Requirements
3.1 Runtime
| Requirement | Detail |
|---|---|
| Python | 3.9–3.12 (dependency pins target this range) |
| Node.js | ≥18 (only if using the Node stack; see engines in the bundled package.json) |
| OS | Linux, macOS, or Windows (paths handled cross-platform) |
| Memory | Modest; the prod Compose overlay caps the container at 512 MB |
| Disk | Source JSON collection + ~tens of MB for the SQLite index |
3.2 Python dependencies (requirements.txt)
| Package | Pinned version | Role |
|---|---|---|
fastapi |
0.109.0 | Web framework for the core API |
uvicorn[standard] |
0.27.0 | ASGI server (development / single-process) |
pydantic |
2.5.3 | Request/response models and validation |
gunicorn |
21.2.0 | Production process manager (multi-worker) |
PyJWT |
2.8.0 | JWT tokens — used only by the standalone user-management module |
passlib[bcrypt] |
1.7.4 | Password hashing — standalone user-management module |
python-multipart |
0.0.9 | Form parsing (auth endpoints) |
httpx |
0.26.0 | Outbound HTTP — used by the standalone integration hub |
requests |
2.31.0 | Outbound HTTP (sync) |
psutil |
5.9.8 | System metrics — standalone performance monitor |
email-validator |
2.1.0 | Email validation — standalone user-management module |
Note
Several of these packages (
PyJWT,passlib,psutil,email-validator,python-multipart) are required only by the standalone feature modules in Section 9, not by the core search server. The core server needs onlyfastapi,uvicorn,pydantic, and the Python standard library (sqlite3).
4. Configuration Reference
All configuration is via environment variables and command-line flags. There is no config file for the core server.
4.1 Environment variables
| Variable | Default | Consumed by | Purpose |
|---|---|---|---|
WORKFLOW_DB_PATH |
workflows.db |
workflow_db.py |
Path to the SQLite database. run.py overrides this to database/workflows.db at startup. |
CI |
(unset) | run.py |
If true/1/yes, skip indexing on startup (same as --skip-index). |
ADMIN_TOKEN |
(unset) | api_server.py |
Gates POST /api/reindex. If unset, the reindex endpoint is disabled (returns 503). |
NODE_ENV |
(unset) | src/server.js |
Standard Node environment switch for the Node stack. |
JWT_SECRET_KEY |
(empty) | src/user_management.py (standalone) |
Secret for signing JWTs in the optional auth module. |
ADMIN_PASSWORD |
(empty) | src/user_management.py (standalone) |
Initial admin password for the optional auth module. |
The Docker Compose overlays set additional process-level variables (ENVIRONMENT,
LOG_LEVEL, DEBUG, RELOAD, ENABLE_METRICS, MAX_WORKERS) consumed by the container
entrypoint/compose rather than the Python code directly.
Caution
ADMIN_TOKENis checked with a plain string comparison and passed as a query parameter (?admin_token=...) to/api/reindex. Query parameters are commonly logged by proxies and servers. If you enable reindexing, terminate TLS in front of the service, restrict the endpoint at the proxy, and rotate the token if it may have been logged.
4.2 Command-line flags
run.py (primary launcher):
| Flag | Default | Effect |
|---|---|---|
--host |
127.0.0.1 |
Bind address |
--port |
8000 |
Bind port |
--reindex |
off | Force full re-index before serving |
--dev |
off | Auto-reload (passes reload=True to Uvicorn) |
--skip-index |
off | Start without indexing |
api_server.py (run directly, bypassing run.py):
| Flag | Default | Effect |
|---|---|---|
--host |
127.0.0.1 |
Bind address |
--port |
8000 |
Bind port |
--reload |
off | Auto-reload |
workflow_db.py (DB CLI): --index, --force, --search "TEXT", --stats.
src/index-workflows.js (Node indexer): -f/--force, --stats.
src/server.js (Node server): -p/--port, -h/--host, --dev.
5. Database & Indexing Internals
5.1 Schema
The database (workflow_db.py → init_database()) creates one base table, one FTS5 virtual
table, five indexes, and three sync triggers.
workflows table:
| Column | Type | Notes |
|---|---|---|
id |
INTEGER PK AUTOINCREMENT | |
filename |
TEXT UNIQUE NOT NULL | Basename of the workflow file (also the API key) |
name |
TEXT NOT NULL | Human-readable name (from JSON name or derived from filename) |
workflow_id |
TEXT | The id field inside the workflow JSON |
active |
BOOLEAN DEFAULT 0 | From the workflow's active flag |
description |
TEXT | From JSON description, else generated |
trigger_type |
TEXT | Manual / Webhook / Scheduled / Complex |
complexity |
TEXT | low / medium / high |
node_count |
INTEGER | Number of nodes |
integrations |
TEXT | JSON array of detected services |
tags |
TEXT | JSON array |
created_at, updated_at |
TEXT | From the workflow JSON |
file_hash |
TEXT | MD5 of the file (change detection) |
file_size |
INTEGER | Bytes |
analyzed_at |
TIMESTAMP | When indexed |
workflows_fts — FTS5 virtual table over filename, name, description, integrations, tags, configured as an external-content table (content=workflows,
content_rowid=id). Three triggers (workflows_ai/workflows_ad/workflows_au) keep it in
sync on insert/delete/update.
Indexes: idx_trigger_type, idx_complexity, idx_active, idx_node_count,
idx_filename.
Performance PRAGMAs set at init: journal_mode=WAL, synchronous=NORMAL,
cache_size=10000, temp_store=MEMORY.
5.2 Indexing pipeline (index_all_workflows)
- Recursively glob
workflows/**/*.json. - For each file: unless
force_reindex, compute MD5 and compare to the storedfile_hash; skip if unchanged. analyze_workflow_file()parses JSON and extracts: node count → complexity bucket; trigger type and integrations from node types (analyze_nodes()); a name (JSON name, orformat_workflow_name()from the filename); and a description (JSON description, orgenerate_description()).INSERT OR REPLACEintoworkflows; triggers update the FTS index.- Returns
{processed, skipped, errors}.
Tip
Indexing is idempotent and incremental. Re-running after adding a handful of files reprocesses only those files. Use
--forceto rebuild after changing the analysis logic itself (e.g. the service-mapping table), since unchanged files would otherwise be skipped.
5.3 Integration & trigger detection
analyze_nodes() maps node type strings to friendly service names via a large
service_mappings dictionary (e.g. n8n-nodes-base.slack → "Slack", @n8n/... namespaced
nodes handled too). Utility nodes (set, if, merge, code, stickyNote, wait,
filter, etc.) map to None and are excluded from the integration list. Trigger type is
inferred from node types: a webhook node → Webhook, cron/schedule → Scheduled, otherwise
Manual; a large multi-integration workflow (>10 nodes and >3 integrations) is promoted
to Complex.
get_service_categories() groups services into broad buckets (messaging, email,
cloud_storage, database, project_management, ai_ml, social_media, ecommerce, analytics,
calendar_tasks, forms, development) used by search_by_category() and the
/api/workflows/category/{category} endpoint.
Note
The user-facing display categories shown in the UI (AI Agent Development, CRM & Sales, …) come from
context/*.jsonand the/api/categoriesendpoint. The internalget_service_categories()buckets above are a separate, code-level grouping used by the category-search endpoint. They are related but not identical lists.
6. Core API Reference
Served by api_server.py (FastAPI app titled "N8N Workflow Documentation API", version
2.0.0). All responses are JSON unless noted. Interactive docs at /docs, OpenAPI schema at
/openapi.json.
| Method | Path | Auth | Notes |
|---|---|---|---|
GET |
/ |
none | Serves static/index.html (or a setup-required page if missing) |
GET |
/health |
none | {"status":"healthy", ...} |
GET |
/api/stats |
none | StatsResponse — totals, triggers, complexity, integrations |
GET |
/api/workflows |
none | Search/filter; params q, trigger, complexity, active_only, page, per_page |
GET |
/api/workflows/{filename} |
none | Metadata + raw JSON; rate-limited, filename-validated |
GET |
/api/workflows/{filename}/download |
none | FileResponse of the JSON; rate-limited, filename-validated |
GET |
/api/workflows/{filename}/diagram |
none | Mermaid flowchart code; rate-limited, filename-validated |
POST |
/api/reindex |
ADMIN_TOKEN |
Background re-index; ?force=, ?admin_token=; rate-limited |
GET |
/api/integrations |
none | Integration count summary |
GET |
/api/categories |
none | Category list (from context/*.json) |
GET |
/api/category-mappings |
none | filename→category map for client-side filtering |
GET |
/api/workflows/category/{category} |
none | Workflows in a service category |
per_page is bounded 1–100; page ≥ 1. The search endpoint returns a
SearchResponse envelope (workflows, total, page, per_page, pages, query, filters).
Middleware: GZipMiddleware (compresses responses ≥ 1000 bytes) and CORSMiddleware
(restricted — see Section 7). A global exception handler returns
{"detail": "Internal server error: ..."} with HTTP 500.
Warning
The global exception handler echoes the exception string into the response body. This can leak internal detail (paths, SQL fragments) to clients. Consider replacing it with a generic message and server-side logging before exposing the service publicly. See Section 11 (Improvements).
6.1 Status codes you'll see
| Code | Where | Meaning |
|---|---|---|
| 400 | filename endpoints | Invalid filename format (failed validation) |
| 401 | /api/reindex |
Invalid authentication token |
| 404 | filename endpoints | Workflow not in DB or not on disk |
| 429 | rate-limited endpoints | Rate limit exceeded (>60 req/min/IP) |
| 503 | /api/reindex |
Endpoint disabled (no ADMIN_TOKEN set) |
| 500 | any | Unhandled error (body includes exception text) |
7. Security
7.1 Path-traversal protection (validate_filename)
The three filename-bearing endpoints (detail, download, diagram) all call
validate_filename() before touching the filesystem. It:
- Decodes the input up to 3 times to defeat nested URL-encoding (
%252e%252e). - Rejects any string containing
.., slashes/backslashes, null bytes, newlines,~,:,|,<,>,*,?,$,;,&. - Rejects absolute paths and Windows drive letters.
- Requires the name to match
^[a-zA-Z0-9_\-]+\.json$.
As defense in depth, after locating a candidate file the handlers call
Path.resolve().relative_to(workflows_path) and refuse anything resolving outside the
workflows/ tree. The repository ships test_security.sh, which fires a battery of
traversal payloads and asserts they're blocked (400/404), plus a positive-path download test.
Important
Because filenames are restricted to
[A-Za-z0-9_-].json, any workflow file whose name contains spaces, dots (beyond the extension), or other characters will be un-downloadable through the API even though it indexes fine. Keep workflow filenames within that charset.
7.2 Rate limiting
check_rate_limit() keeps a per-IP in-memory list of request timestamps and enforces
MAX_REQUESTS_PER_MINUTE = 60. It is applied to the detail, download, diagram, and reindex
endpoints.
Caution
Rate-limit state is an in-process dict. It is not shared across workers or replicas and resets on restart, and it keys on
request.client.host— which behind a reverse proxy is the proxy's IP unless you configure trusted forwarded headers. For real protection at scale, enforce rate limiting at the proxy/edge instead of relying on this.
7.3 CORS
api_server.py restricts CORS to an explicit allowlist: http://localhost:3000,
:8000, :8080, https://zie619.github.io, and a Render community URL — with methods
limited to GET/POST and headers to Content-Type/Authorization. Edit
ALLOWED_ORIGINS to add your own front-end origin.
Note
The standalone
enhanced_api.pymodule, by contrast, usesallow_origins=["*"]. That is another reason to treat the standalone modules (Section 9) as not production-ready as-is.
7.4 Authentication posture
The core server has no authentication for browsing or for the read endpoints — it is
designed to serve a public, read-only catalog. The only privileged action is
/api/reindex, gated by ADMIN_TOKEN. Login/JWT functionality exists only in the optional
src/user_management.py module (Section 9), which is not mounted by default.
7.5 Hardening checklist
- [ ] Run behind a TLS-terminating reverse proxy (the prod Compose overlay uses Traefik).
- [ ] Add proxy-level auth (e.g. Traefik basicauth) if the catalog should not be public.
- [ ] Leave
ADMIN_TOKENunset unless you actively need remote reindexing; prefer reindexing via CLI on the host. - [ ] Restrict
/api/reindexat the proxy even whenADMIN_TOKENis set. - [ ] Replace the verbose global exception handler before public exposure.
- [ ] Enforce rate limiting at the edge, not just in-process.
- [ ] Mount
workflows/read-only into the container (the prod overlay already does:./workflows:/app/workflows:ro).
8. Deployment & Operations
8.1 Docker / Docker Compose
The repo provides a base docker-compose.yml plus two overlays:
docker-compose.dev.yml— bind-mounts the source for live editing, runspython run.py --host 0.0.0.0 --port 8000 --dev, exposes 8000/8001, and offers optionaldb-admin(Adminer) andfile-watcherprofiles.docker-compose.prod.yml—restart: always, production env vars (ENVIRONMENT=production,LOG_LEVEL=warning,ENABLE_METRICS=true,MAX_WORKERS=4), named volumes for the DB and logs, a read-only mount ofworkflows/, resource limits (512 MB / 0.5 CPU), Traefik labels for TLS + basicauth, and optional Prometheus monitoring.
One-command bring-up (build, start, health-check, open browser):
./run-as-docker-container.sh
Manual:
docker compose up -d --build # base
docker compose -f docker-compose.yml -f docker-compose.dev.yml up # dev
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d # prod
docker compose logs -f
docker compose down
8.2 Kubernetes & Helm
k8s/contains raw Kubernetes manifests.helm/workflows-docs/is a Helm chart (withtemplates/) for templated deployment.
Use the Helm chart for parameterized installs; fall back to the raw manifests for a quick
kubectl apply -f k8s/.
8.3 Production server
For production, prefer Gunicorn (pinned in requirements.txt) managing Uvicorn workers
rather than the single-process uvicorn.run() used by run.py:
gunicorn api_server:app -k uvicorn.workers.UvicornWorker -w 4 -b 0.0.0.0:8000
Warning
The in-process rate limiter and the empty-DB auto-index logic assume a single process. With multiple Gunicorn workers, ensure the database is already indexed before starting (run
python workflow_db.py --indexfirst, or start once withrun.pyto build it), so workers don't race to index on first request.
8.4 Routine operations
| Task | Command |
|---|---|
| Build/refresh index | python workflow_db.py --index (add --force to rebuild) |
| Check catalog stats | python workflow_db.py --stats or GET /api/stats |
| Health check | GET /health |
| Remote reindex (if enabled) | POST /api/reindex?admin_token=$ADMIN_TOKEN&force=true |
| Back up state | Copy the database/workflows.db* files (include WAL/SHM) or just re-index from workflows/ |
| Rotate / reset DB | Delete database/workflows.db*, then re-index |
Tip
The database is fully derivable from
workflows/. The simplest disaster recovery is to keep the workflow JSON files under version control and re-index — no database backup strategy is strictly required.
8.5 The static / GitHub Pages mirror
docs/ is a static export intended for GitHub Pages (zie619.github.io). Its
docs/js/search.js loads a pre-built api/search-index.json and does client-side filtering
— there is no live server in that mode. docs/api/*.json (stats.json,
integrations.json, categories.json, metadata.json) are the static data snapshots that
front end consumes. Regenerate them when you publish a new static build.
9. Optional / Standalone Feature Modules
Important
Everything in this section lives under
src/*.py, builds its own FastAPI application object, and is not imported or mounted byapi_server.py. Running the core server does not expose these endpoints. They appear to be experimental/auxiliary and are documented here for completeness and for developers who may wish to wire them in. Treat them as not production-ready without review (permissive CORS, query-string secrets, simulated data — noted below).
| Module | App object / port | Endpoints (prefix) | Notes |
|---|---|---|---|
ai_assistant.py |
ai_app @ :8001 |
POST /chat, GET /chat/interface |
Keyword/intent heuristic search — not an LLM. Returns matched workflows + canned suggestions. |
analytics_engine.py |
analytics_app @ :8002 |
/analytics/overview, /trends, /insights, /dashboard |
Real DB aggregations except get_trend_analysis(), which returns hardcoded simulated values. |
enhanced_api.py |
EnhancedAPI.app |
/api/v2/workflows, /api/v2/workflows/search, /api/v2/recommendations, /api/v2/analytics/* |
CORS *. Imports community features. |
community_features.py |
helper create_community_api_endpoints(app) |
/api/workflows/{id}/rate, /ratings, /stats, /view, /download, /top-rated, /most-popular |
Adds ratings/reviews/views/collections tables to the SQLite DB. |
integration_hub.py |
own app | /integrations/{github,slack,discord,airtable,notion}, /webhooks/{endpoint}, /integrations/status |
Outbound sync helpers via httpx/requests. |
performance_monitor.py |
own app | /monitor/metrics, /history, /alerts, /dashboard, WebSocket |
Uses psutil to sample system metrics. |
user_management.py |
own app | /auth/{register,login,me}, /users CRUD, /auth/dashboard |
JWT (PyJWT) + bcrypt (passlib). Creates a default admin. Requires JWT_SECRET_KEY, ADMIN_PASSWORD. |
9.1 Community features data model
If wired in, community_features.py adds these SQLite tables alongside workflows:
workflow_ratings (1–5 star, unique per (workflow_id,user_id)), workflow_stats
(aggregate ratings/views/downloads), user_profiles, workflow_collections (favorites,
JSON id array), and workflow_comments (threaded via parent_id). Ratings outside 1–5 raise
ValueError('Rating must be between 1 and 5').
9.2 Wiring one in (developer note)
These modules expose either a module-level app or a create_* helper. To integrate, e.g.,
community endpoints into the main server you would import and call
create_community_api_endpoints(app) against the api_server.py app and run a single
process. Before doing so, review CORS, auth, secret handling, and the SQL-string building in
ai_assistant.search_workflows_intelligent() (see Section 11).
10. Code Quality & Conventions
Strengths observed in the source:
- Consistent, well-commented modules with docstrings on public functions.
- Pydantic models (
WorkflowSummary,SearchResponse,StatsResponse) give the core API a typed, self-documenting contract and power the/docsUI. - Parameterized SQL in the core search/stats/category paths (no string interpolation of user input).
- Deliberate security work at the file boundary, with a dedicated test script.
- The Python and Node implementations are kept conceptually aligned (same schema, same
complexity thresholds, same
format_workflow_namerules).
Patterns & conventions:
- The
filename(basename) is the public identity of a workflow throughout the API. - Complexity is a pure function of node count (≤5 / ≤15 / else).
- JSON-encoded arrays (
integrations,tags) are stored as TEXT and parsed on read. - Connections open and close per query (no shared pool) — fine at this scale, simple to reason about.
11. Potential Improvements & Tech Debt
These are observations from the source, prioritized for an operator/maintainer.
Caution
Bug —
search_workflows_intelligent()inai_assistant.py. When the detected intent is"integration", the code doesparams.append('Webhook')butparamsis never defined in that scope (and the subsequent query is executed without parameters), which will raise aNameError/parameter mismatch. This standalone module's intent-based branch is effectively broken. Fix before wiring the AI assistant in.
Warning
SQL string interpolation in the standalone AI module.
ai_assistant.pybuilds itsWHEREclause by interpolating extracted keywords directly into SQL (name LIKE '%{keyword}%'). Keywords are derived from a fixed vocabulary so exploitation is limited, but this pattern should be converted to parameterized queries before any production use. The core server does not have this issue.
Other items:
- Verbose global exception handler (
api_server.py) leaks exception text to clients — replace with a generic message + server-side logging. /api/reindextoken via query string — move to a header (Authorization/X-Admin-Token) to avoid proxy/access-log leakage, and use a constant-time comparison.- In-process rate limiting — does not survive restarts or scale across workers; move to the edge or a shared store.
/api/integrationsreturns an empty list with only a count — it could surface the ranked integration list already present indocs/api/integrations.json.- Simulated analytics —
analytics_engine.get_trend_analysis()returns hardcoded values; either implement real historical tracking or label it clearly as a demo. - Repository hygiene — the unrelated
medcards-ai/Next.js subproject and several experimentalsrc/modules inflate the repo and dependency surface; consider splitting them into separate repositories. - Known issue #156 (
No module named 'workflow_db') stems from running outside the repo root; documenting the working directory requirement (done in the User Guide) mitigates it.
12. Testing
| Script | What it covers |
|---|---|
test_security.sh |
Fires path-traversal payloads at /download and asserts 400/404; verifies a valid download returns 200. Requires the server running on :8000. |
test_api.sh |
Smoke-tests search, categories, integrations, complexity filter, pagination, and single-workflow fetch via curl. |
test_workflows.py |
Validates sample workflow JSON files (presence of name, nodes, connections) across representative categories; reports a per-category pass rate. |
Run them against a locally running instance:
python run.py --skip-index & # or with a built DB
bash test_api.sh
bash test_security.sh
python test_workflows.py
Note
These are shell/script smoke tests, not a unit-test suite. There is no
pytestconfiguration in the core project; coverage of the indexing/search logic is via the live API scripts above.
13. Quick Reference
Launch (Python):
python run.py [--host H] [--port P] [--reindex] [--dev] [--skip-index]
python api_server.py [--host H] [--port P] [--reload]
Database CLI:
python workflow_db.py [--index] [--force] [--search "TEXT"] [--stats]
Node stack:
node src/index-workflows.js [--force] [--stats]
node src/server.js [-p PORT] [-h HOST] [--dev]
Production (Gunicorn):
python workflow_db.py --index # build DB first
gunicorn api_server:app -k uvicorn.workers.UvicornWorker -w 4 -b 0.0.0.0:8000
Containers:
./run-as-docker-container.sh
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Environment variables:
| Var | Purpose |
|---|---|
WORKFLOW_DB_PATH |
SQLite path (default workflows.db; run.py sets database/workflows.db) |
CI |
Skip indexing on startup |
ADMIN_TOKEN |
Enables/guards POST /api/reindex |
NODE_ENV |
Node server environment |
JWT_SECRET_KEY, ADMIN_PASSWORD |
Standalone user-management module only |
Core endpoints: /, /health, /api/stats, /api/workflows,
/api/workflows/{filename}[/download|/diagram], /api/reindex, /api/integrations,
/api/categories, /api/category-mappings, /api/workflows/category/{category}, /docs.