Service
FastAPI composition, async ingestion, workflow catalog and compatibility APIs, authentication boundaries, and default-off execution.
ragspine.service is an optional composition root over the engine. Install [service] to get
FastAPI, uvicorn, RQ, and Redis support. The service opens request/job-owned stores, injects
providers and matchers through typed seams, and returns opaque errors without tracebacks.
App factory
from ragspine.service.api.app import create_app
app = create_app(
config=None,
provider=None,
queue=None,
faq_cache=None,
workflow_matcher=None,
)Omitted dependencies are assembled from ServiceConfig.from_env(). Instances are held on
app.state, which also makes them straightforward to replace in tests. When studio_dir points
to an existing directory, its static build is mounted at /studio; an empty or missing directory
does not add that route.
Endpoint groups
| Group | Paths | Executes work? | Authentication |
|---|---|---|---|
| engine | /healthz, /readyz, /v1/ask*, ingestion/jobs | ask and queue operations | none built in |
| catalog | /v1/workflow-templates*, /v1/workflow-scaffold | no | none built in |
| compiler/converter | /v1/dify/analyze, /compile, /v1/n8n/convert, /v1/topology | no | none built in |
| internal execution | /v1/dify/run*, /v1/n8n/run | yes, when enabled | execution flag; deploy behind your own auth |
| Dify-compatible public API | /v1/workflows/run*, /v1/info, /v1/parameters | run route when enabled | Bearer app key |
| n8n-compatible public API | /api/v1/* | CRUD; webhook can run when enabled | X-N8N-API-KEY |
| n8n webhook | /webhook/{path} | active matching workflow, when enabled | intentionally none |
The general engine and internal /v1/dify/* routes do not provide user authentication. Bind to
localhost/private networks or add an authenticated reverse proxy before public exposure.
Engine and ingestion
POST /v1/ask and /v1/ask/stream run the FAQ gate, intent/security decisions, retrieval,
anti-fabrication rewrite, and provenance assembly. The SSE endpoint finishes all guards before it
opens the stream. AskRequest.history is optional generation context only and cannot become
retrieval evidence.
Structured ingestion accepts .xlsx, .xlsm, .pptx, and .pdf; narrative jobs accept
.pptx and .pdf. RAGSPINE_ALLOWED_UPLOAD_ROOT constrains resolved paths. The API enqueues a
dotted job reference; the worker revalidates paths and owns its own SQLite connections.
FakeQueue is synchronous/in-memory for tests. RQQueue is the Redis-backed production adapter.
Queue statuses are queued, started, finished, and failed; reports contain counts/statuses,
not raw fact values or chunk text.
Workflow catalog and compiler
Catalog list pages contain metadata only. Detail and scaffold responses add the canonical workflow, Dify YAML, and preview v1. Scaffolding may match semantically, fall back to lexical matching, or generate a fixed graph; it never executes the output.
Dify analyze/compile bodies contain exactly one of a canonical workflow object or legacy yaml
string; unknown fields are rejected. Analyze is static only. Compile returns a Python code string
and never runs it. n8n conversion is also pure and returns explicit compatibility warnings.
Selected workflow POST request bodies are bounded to 2 MiB before FastAPI buffers them:
/v1/workflow-scaffold and /v1/dify/{analyze,compile,run,run/jobs}. The decoded workflow document
has the tighter 1 MiB parser limit. Validation-error responses remove Pydantic's echoed input and
ctx fields so a rejected secret is not reflected.
Default-off execution
RAGSPINE_DIFY_RUN_ENABLED defaults to false. Until explicitly enabled,
/v1/dify/run, /v1/dify/run/jobs, /v1/n8n/run, Dify-compatible workflow execution, and n8n
webhook execution fail closed. The provider, model, base URL, and Dify app workflow files come from
server configuration; clients cannot inject a provider expression or key.
Execution is compiled and checked by the L0 static gate. The default inprocess isolation applies
restricted builtins/imports and a timeout, but is not an OS sandbox. subprocess adds Linux resource
limits; on macOS and Windows it falls back to the in-process restrictions. Treat untrusted workflow
execution as a separate hardened service boundary.
Dify-compatible public API
RAGSPINE_DIFY_PUBLIC_APPS is a semicolon-separated registry such as
app-key=/srv/workflows/support.yml;other-key=/srv/workflows/report.yml. Requests use
Authorization: Bearer app-key. If the registry is absent, invalid, or the key is unknown, these
routes return 401.
The API supports POST /v1/workflows/run, GET /v1/workflows/run/{id}, GET /v1/info, and
GET /v1/parameters. Run requests supply inputs, user, and response_mode; they do not supply
the workflow. Streaming uses SSE after execution and replays trace events. Run-detail data is a
process-local LRU of the latest 100 runs and is isolated per app key; it is not durable history.
n8n-compatible public API and webhooks
Set RAGSPINE_N8N_API_KEY; clients send X-N8N-API-KEY. If no key is configured, every
/api/v1/* request returns 401. Workflow CRUD and activation, plus execution list/get/delete,
are backed by the filesystem root RAGSPINE_N8N_STORE_PATH (default data/n8n_store). List limits
default to 100, clamp to 1–250, and use cursors.
GET|POST /webhook/{path} deliberately has no API-key check, matching webhook behavior. It matches
only an active workflow's webhook node and still requires execution to be enabled. Query parameters
and a JSON-object body form inputs, with body keys winning. Put ingress controls, TLS, rate limiting,
and request-size policy in front of this route.
Run the service
From the repository root:
RAGSPINE_DB_PATH=data/fact_metric.db \
RAGSPINE_CHUNK_DB_PATH=data/chunks.db \
.venv/bin/python scripts/run_server.py --host 127.0.0.1 --port 8000
# separate process; Redis required
RAGSPINE_REDIS_URL=redis://localhost:6379/0 \
.venv/bin/python scripts/run_worker.pySee HTTP API for exact routes and Configuration for the environment surface.