External interfaces, candidate 0.1.0.dev8
This candidate is local development software. Synthetic acceptance never proves live provider acceptance. No interface publishes stories or starts provider writes as part of ingestion.
Service ownership and startup
The consuming application or its deployment operator owns service startup, shutdown, exact version pins, trusted configuration, database backups, tokens, and any reverse proxy. A TypeScript client does not start or stop a server. Install the standalone package with its service extra, pinned to the same candidate as the client:
py -m pip install "C:\connectors\quorumrelay-0.1.0.dev8-py3-none-any.whl[service]"
quorumrelay serve --config C:\app\connector-service.json --database C:\app\connector-state.sqlite
The package has not been published by this work. Replace the example wheel path
with the candidate whose hash you verified; it does not use a package index.
For checkout development use pip install -e ".[service]" from this repository.
python -m quorumrelay is equivalent to the console entry point.
Keep the service in its own supervised process; Ctrl+C shuts it down locally.
Restart it using the same database, config, and exact version. Check
authenticated GET /v1/info after startup and compare version/schema_version
with the consumer's pinned deployment requirements before ingesting.
examples/service.mock.json contains clearly synthetic, non-deployable tokens
for two newsrooms. Replace them with separately generated high-entropy tokens
for real deployment. Protect configuration with OS filesystem permissions.
tokens maps each bearer to trusted principal, client_id, consumer tenant,
operations, and optional write authorization. Select the bearer from the
owning newsroom's server-side configuration. Never select it from an arbitrary
request field or send it to an upstream provider.
Caller identity is never accepted in operation JSON or headers such as
X-Tenant. Consumer tenant scopes captures/jobs/plans/receipts. Provider source
namespace remains the provider's stable source identity, not a newsroom ID.
Upstream credentials belong in private trusted engine context, separate from
service authentication. Do not put them in source URLs or public requests.
The default bind is loopback 127.0.0.1:8765. No network deployment is started
by these examples. Non-loopback binding requires explicit allow_network,
authenticated token policy and an explicit Host allowlist. Restrict
allowed_origins; an unexpected Origin is rejected even on loopback.
Terminate HTTPS at an operator-owned reverse proxy. MCP authenticated network
deployment additionally requires OAuth protected-resource metadata and an
externally supplied, audience-validating token verifier; see MCP documentation.
JSON operations and CLI
POST /v1/operations/OPERATION accepts exactly the shared engine request object.
Common operations and schemas are in ENGINE-CONTRACT.md; writes are in
WRITE-CONTRACT.md. /openapi.json is the actual OpenAPI 3.1 document.
Every response preserves the full schema 2.0.0 envelope. Inspect status,
errors, and coverage, including partial records, failed pages and truncation.
An unknown implementation is unsupported, not an empty successful search.
'{}' | quorumrelay providers
quorumrelay discover --request C:\captures\request.json --content C:\captures\calendar.json
quorumrelay search --jsonl --request C:\captures\queries.jsonl --database C:\app\connector-state.sqlite
Requests come from stdin unless --request selects a saved file. --jsonl
means one complete request/result per nonblank line, not an array silently
split into records. --content selects saved parser bytes explicitly.
--file-root restricts requested local input paths, resolving symlinks/junctions.
The CLI does not expose server-side paths over HTTP.
For large provider submissions, declare a file part's part, filename and
content_type in the selected change, then use
--write-file CHANGE_KEY PART C:\captures\document.pdf with plan_write.
The CLI stages that explicit local file within --file-root, binds its hash and
length into the scoped plan, and enforces transfer limits. It does not base64
encode the file into request JSON or commit the plan. This requires configured
durable storage and the caller's existing write policy. Each option must match
one metadata-only file part; existing inline/handle inputs cannot be overwritten.
--config is trusted process-owner policy; operation JSON cannot grant access.
JSONL shares one engine/store within the process. Stdout contains JSON only;
diagnostics go to stderr. Exit codes: completed 0, partial 2, cancelled 3,
failed 4, unsupported 5, invalid framing/configuration 64. Bulk returns the
highest observed code while preserving each result.
Files and long operations
POST /v1/files accepts multipart part file or application/octet-stream.
The service returns an opaque file_handle, never a local path.
Pass that handle in an operation request instead of content_base64.
Handles are scoped to authenticated principal/client/tenant. Retrieval uses
GET /v1/files/HANDLE and streams bytes with a safe fixed filename.
Parser inputs default to 16 MiB; uploaded/downloaded documents default to
200 MiB. A large authorized download is not evidence that it can be parsed.
Both limits apply to actual bytes, not solely Content-Length.
For a neutral interchange file, call import_preview or import_bundle with
{"format":"zip","file_handle":"..."}. ZIP and JSONL inputs use the transfer
ceiling while each metadata document/JSONL line retains its parser ceiling.
The handle is resolved in the authenticated caller's storage scope and its bytes
are verified before import. CLI equivalents use --content C:\captures\bundle.zip
with {"format":"zip"} as the JSON request. Parser-only source captures retain
their original limit. A plan_write caller may stage attachment inputs without
receiving fetch, download or commit permission.
JSON and JSONL exports resolve stored original captures into portable inline bytes, validating their recorded hash and size. Text-format parser limits still apply; ZIP carries large original files separately. Export never silently drops originals to fit a text format.
POST /v1/jobs/OPERATION submits bounded work and returns HTTP 202 with a
partial envelope, operation_id, and running data state. This is not
completed acceptance. Poll shared status with that ID; final results remain
in its data. Use shared cancel to request cancellation. Cancelled work keeps
already completed steps and write receipts; it never deletes provider data.
State uses the configured scoped store. After process restart a previously
running job may need consumer review/retry, rather than a fictional resumed
worker. Writes reconcile uncertain intents according to their core contract.
TypeScript and an unrelated fifth client
The package is quorumrelay-client@0.1.0-dev.8. For local development
use a file: dependency to client/. To use it without this checkout or any
reference product, build and npm pack the client, then install its .tgz in
your own project. Generated types come from the packaged core schemas.
import { QuorumRelayClient, type RecordEnvelope } from "quorumrelay-client";
const client = new QuorumRelayClient({
baseUrl: process.env.CIVIC_CONNECTOR_URL!,
token: process.env.CIVIC_CONNECTOR_TOKEN,
});
const result = await client.execute("providers", {});
if (result.status !== "completed") {
console.error(result.errors, result.coverage);
}
Service HTTP failures throw ServiceError with the complete envelope.
Engine partial/failed/unsupported results remain envelopes, not stripped arrays.
Abort/timeout/network failures remain distinct native errors. Provide {signal}
to public client methods when the consumer owns cancellation.
Check the service version before use and pin both deployment artifacts.