Spaces:
Running
Running
| title: Synderesis API | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| license: other | |
| Public Docker Space for the Synderesis FastAPI proxy. | |
| Browser API 1.10 adds `execution_mode` (`answer` by default or `task`) on | |
| supported Catholic LLM Synod deployments. Spark and Pro Task requests are | |
| limited to six controller decisions and four app-executed tool calls: official | |
| source search and, when enabled by the user, the fixed Exa web-search path. | |
| There is no shell, arbitrary URL or file fetch, repository browser, or | |
| controller-selected provider tool. Research remains Answer-only. | |
| ## Spark and Pro routing | |
| Catholic chat accepts validated `model_tier` and `reasoning_effort` values. | |
| Spark preserves the existing Synderesis experience, while Pro uses the dedicated | |
| Synderesis premium route. Browser replies expose product names and aggregate | |
| usage only. | |
| The Pro provider profile keeps exact-parameter routing enabled and omits | |
| `temperature`, `top_p`, and `seed`, which are not consistently supported across | |
| its compatible endpoint set. It sorts healthy compatible endpoints by | |
| throughput and retains fallback routing. Keep the request-profile regression | |
| aligned with current endpoint metadata. | |
| Browser chat uses a 2,048-token visible-answer target by default and accepts an | |
| explicit target up to 4,096. OpenRouter counts hidden reasoning inside | |
| `max_tokens`, so the server maps each product effort to the nearest explicitly | |
| supported upstream level and expands the internal completion ceiling using the | |
| documented reasoning share. Candidate and grounding-verifier targets remain | |
| separately capped, quota reservation uses the same expanded values, and | |
| High/Max calls receive longer timeouts. Do not collapse these back to the | |
| inherited 320-token API default: that starves structured visible output, | |
| especially at Max. | |
| The default is Spark/Minimal. Legacy `thinkingMode` browser storage migrates to | |
| High or Minimal. Research remains isolated and ignores these fields. | |
| Final answers default to thorough, self-contained explanations unless the user | |
| asks for brevity; refusals remain short. | |
| ### Catholic chat connections | |
| The Catholic Spark and Pro workspace can advertise two optional account | |
| connections: | |
| - A read-only GitHub App for user-selected repositories. The App must have only | |
| Metadata read and Contents read. The callback is | |
| `https://www.synderesis.eu/v1/auth/github/callback`; the webhook is | |
| `https://www.synderesis.eu/v1/integrations/github/webhook`. Configure installation, | |
| installation-repositories, and authorization lifecycle events, use the | |
| callback as the post-install Setup URL, and enable expiring user access | |
| tokens. | |
| - A bring-your-own OpenRouter key. It is saved only through the dedicated | |
| credential endpoint and is opt-in for each browser tab and Catholic chat | |
| request. | |
| Both depend on a versioned AES-256-GCM keyring. Keep the following values in the | |
| repository's age-encrypted `.env.age`; a regular Catholic LLM Synod deploy sends | |
| non-empty values to the Space only as secrets: | |
| ```text | |
| SYNDERESIS_CREDENTIAL_ENCRYPTION_KEYS_JSON={"v1":"<base64url-32-byte-key>"} | |
| SYNDERESIS_CREDENTIAL_ENCRYPTION_ACTIVE_KEY_ID=v1 | |
| SYNDERESIS_GITHUB_APP_CLIENT_ID=<github-app-client-id> | |
| SYNDERESIS_GITHUB_APP_CLIENT_SECRET=<github-app-client-secret> | |
| SYNDERESIS_GITHUB_APP_SLUG=<github-app-slug> | |
| SYNDERESIS_GITHUB_OAUTH_CALLBACK_URL=https://www.synderesis.eu/v1/auth/github/callback | |
| SYNDERESIS_GITHUB_WEBHOOK_SECRET=<random-webhook-secret> | |
| ``` | |
| Never delete an old keyring entry while database rows still use its key ID. | |
| Change the active ID to rotate new writes, reconnect or re-save old credentials, | |
| then retire the old entry. GitHub OAuth access and refresh tokens and remembered | |
| BYOK values are encrypted in SQLite; malformed envelopes fail closed. | |
| Session-scoped BYOK values expire within one hour and are deleted on logout. | |
| GitHub file bytes are fetched only for the current Spark or Pro request and are | |
| not directly stored in the database or browser-history record. Model output can | |
| reproduce repository material, and completed assistant text can then be saved, | |
| shared, or downloaded by the user. `--upload-only` deliberately does not read or | |
| synchronize this secret configuration. A regular Catholic deploy removes stale | |
| connector secrets that are absent from `.env.age`. The connector is not | |
| advertised unless the callback is the canonical URL and the webhook secret has | |
| at least 32 characters. A customer key may fund selected-tier Answer calls and | |
| Task controller/final-answer model calls. The fixed web-search and suitability | |
| review calls remain Synderesis-funded, so a request using both can have mixed | |
| funding. | |
| The Space installs its Python environment from the repository root `pyproject.toml` | |
| and `uv.lock`, so local `uv sync` and the Hugging Face Docker runtime resolve the | |
| same dependency set. The deploy bundle also contains `synderesis_deployment.json`, | |
| which is logged as `SYNDERESIS_DEPLOYMENT_INFO` at startup and propagated to the | |
| API diagnostics endpoint for commit/source-DB traceability. | |
| The Space runs `scripts/serve_huggingface_api.py` under Uvicorn, downloads the official-source | |
| SQLite retrieval database from a private Hugging Face dataset, and calls the | |
| configured model backend server-side. Customer access is restricted by | |
| `X-API-Key`; upstream credentials are stored only as Space secrets. | |
| Rebuild and upload the retrieval database when deploying this version so the | |
| compact Catechism 2357-2359 retrieval note is included in the Space artifact. | |
| Persistent API/account state lives in SQLite under `/data/synderesis_api.sqlite3` | |
| when the Space has the private bucket volume `cuivienen/synderesis-api-state` | |
| mounted read-write at `/data`. The deploy script now preflights that mount before | |
| any remote upload or Space configuration, ensures it by default after the new | |
| bundle is uploaded, and refuses to overwrite any conflicting existing `/data` | |
| volume; pass `--no-state-volume` only when intentionally skipping that step. | |
| The local default SQLite journal mode remains `WAL`, but the Space sets | |
| `SYNDERESIS_SQLITE_JOURNAL_MODE=DELETE` and the container startup path also | |
| defaults to `DELETE`. That rollback-journal mode avoids WAL's `-wal` and `-shm` | |
| sidecar files, which are unsafe on the NFS/FUSE-backed `/data` mount used for | |
| durable Hugging Face bucket storage. | |
| ## Private-Beta browser chat | |
| Keep `SYNDERESIS_SERVICE_LIVE` false during the private Beta. An account-level | |
| `chat_access` grant opens the signed-in `/chat/` workspace and on-demand, | |
| account-scoped server API-key management. Waitlist-only accounts remain blocked, | |
| registration issues no key automatically, and extension-device routes stay | |
| closed. A regular deploy writes | |
| `SYNDERESIS_SERVICE_LIVE=0` explicitly so a stale public-launch value cannot | |
| survive; changing that behavior is a separate launch decision. | |
| The invitation configuration lives in the repository's age-encrypted | |
| `.env.age`: | |
| - `SYNDERESIS_EARLY_ACCESS_INVITE_SECRET` is a stable random value of at least | |
| 32 characters used to derive email-bound invitation codes. It is required for | |
| waitlist invitations. | |
| - `SYNDERESIS_ADMIN_EXPORT_KEY` authenticates the private administrator | |
| endpoints through `X-Admin-Key`. | |
| - `SYNDERESIS_EARLY_ACCESS_EMAILS` is an optional legacy comma-separated static | |
| invitation list. It is not needed for the waitlist workflow. | |
| - `SYNDERESIS_EARLY_ACCESS_MONTHLY_TOKEN_LIMIT_CUSTOMER_ID` optionally names | |
| one generated opaque customer ID for a 1,000,000-token monthly allowance. | |
| Its request caps remain 50/day and 1,500/month; other early-access accounts | |
| remain at 500,000 tokens/month. | |
| The regular deploy path decrypts these values only in memory and stores them as | |
| Hugging Face Space **secrets**. They must never be ordinary Space variables. | |
| An empty static email list clears only that list; a separately configured | |
| invitation secret remains available for waitlist invitations. Removing | |
| `SYNDERESIS_ADMIN_EXPORT_KEY` from encrypted configuration revokes a stale | |
| deployed administrator key on the next regular secret-synchronizing deploy. | |
| The account-specific override is strictly validated before remote mutation and | |
| updates only existing key limits, preserving keys and usage. `--upload-only` | |
| does not read `.env.age` or update these secrets. | |
| Use the private administrator flow to select and prepare invitations: | |
| ```bash | |
| curl -sS 'https://www.synderesis.eu/v1/admin/waitlist?status=pending' \ | |
| -H "X-Admin-Key: ${SYNDERESIS_ADMIN_EXPORT_KEY}" | |
| curl -sS -X POST \ | |
| 'https://www.synderesis.eu/v1/admin/beta-invitations/prepare' \ | |
| -H "X-Admin-Key: ${SYNDERESIS_ADMIN_EXPORT_KEY}" \ | |
| -H 'Content-Type: application/json' \ | |
| --data '{"customer_ids":["acct_example"]}' | |
| ``` | |
| The same administrator credential provides a private monthly usage summary: | |
| ```bash | |
| curl -sS \ | |
| 'https://www.synderesis.eu/v1/admin/usage?period=2026-07&limit=500&include_zero_usage=false' \ | |
| -H 'X-Admin-Key: <ADMIN_EXPORT_KEY>' | |
| ``` | |
| `period` must be UTC `YYYY-MM` and defaults to the current UTC month. `limit` | |
| defaults to 500 and is bounded to 1–2000. Set `include_zero_usage=true` to add | |
| registered accounts with no selected-month activity. The response includes | |
| authoritative totals, matching/returned counts, and deterministic per-user | |
| identity, classification, plan/status, request/token, endpoint, error, and | |
| last-use fields. It intentionally excludes provider/model routing, request | |
| content, credentials, API-key identifiers, and cost ledgers. | |
| `SYNDERESIS_ADMIN_EXPORT_KEY` must remain a Hugging Face Space secret. Never | |
| place a real value in documentation, logs, URLs, commits, or ordinary Space | |
| variables. | |
| Preparation is all-or-nothing for at most 50 active waitlist members. Its | |
| no-store response contains each recipient's email address, private code, | |
| subject, and exact plain-text body. Treat it as sensitive: do not log, publish, | |
| or commit it. Send that body through the operational mailbox. The API | |
| intentionally stores no mailbox credential, plaintext code, or message body. | |
| Only after every selected message has actually been sent, record the exact | |
| `invitation_version` returned by preparation: | |
| ```bash | |
| curl -sS -X POST \ | |
| 'https://www.synderesis.eu/v1/admin/beta-invitations/mark-sent' \ | |
| -H "X-Admin-Key: ${SYNDERESIS_ADMIN_EXPORT_KEY}" \ | |
| -H 'Content-Type: application/json' \ | |
| --data '{"invitations":[{"customer_id":"acct_example","invitation_version":1}]}' | |
| ``` | |
| If a secret rotation or email-binding change reissues the code, its version | |
| increments and its sent state resets. A stale version is rejected rather than | |
| marking the replacement code sent. | |
| The recipient opens `/account/`, signs in with the invited address, and pastes | |
| the code into the Beta invitation card. Never put the code in a URL or query | |
| string. A successful claim persists `chat_access` and consumes the code while | |
| retaining only one-way hashes. An email address alone is never authorization. | |
| Administrators may instead issue one-time transferable registration codes: | |
| ```bash | |
| curl -sS -X POST \ | |
| 'https://www.synderesis.eu/v1/admin/beta-registration-codes' \ | |
| -H "X-Admin-Key: ${SYNDERESIS_ADMIN_EXPORT_KEY}" \ | |
| -H 'Content-Type: application/json' \ | |
| --data '{"count":2,"expires_at":"2026-08-31T23:59:59Z"}' | |
| ``` | |
| The authenticated response is marked `no-store` and is the only place the | |
| plaintext codes appear. Deliver them out of band and do not log or retain the | |
| response. The database stores only a SHA-256 identity, a non-secret prefix, and | |
| lifecycle timestamps. `GET /v1/admin/beta-registration-codes` returns metadata | |
| only; `POST /v1/admin/beta-registration-codes/{prefix}/revoke` revokes an | |
| active unclaimed code. A code may be used by any valid new account during | |
| registration or redeemed by a signed-in account, but exactly once. It grants | |
| `chat_access` and never creates an API key automatically or changes | |
| `SYNDERESIS_SERVICE_LIVE`. After claiming it, the signed-in account may create | |
| its own bounded server API keys. Email-bound invitations above remain | |
| account-bound and retain their existing behavior. | |
| The optional legacy static-email path still prints each configured address's | |
| private code once in regular-deploy JSON output. Deliver those codes with the | |
| same precautions. | |
| The signed-in `/chat/` endpoint accepts up to four request-scoped files of | |
| 5 MB each (5,000,000 bytes), with a 10 MB combined attachment limit. Only the | |
| two authenticated browser-chat upload routes receive the corresponding larger | |
| request-body budget; other API routes retain the 1 MiB body limit. Text inputs share a fair 20,000-character context | |
| budget, capped at 16,000 characters per file, and truncation is returned in | |
| attachment metadata for the page to disclose. Known document parsers cover PDF, | |
| DOCX, PPTX, XLSX, ODT, EPUB, HTML/XML, and RTF. Readable plain text is also | |
| accepted when it has no extension or arrives as `application/octet-stream`; | |
| binary content is rejected after server-side sniffing. | |
| ### Official-source and supplemental-web controls | |
| Catholic browser chat accepts a strict, server-enforced source policy: | |
| ```json | |
| { | |
| "source_policy": { | |
| "official": "auto", | |
| "families": ["catechism", "magisterial"], | |
| "web_search": false, | |
| "web_source_review": true | |
| } | |
| } | |
| ``` | |
| `official` accepts only `auto`, `on`, or `off`. The legacy | |
| `retrieve_sources` Boolean remains compatible by mapping to `on` or `off`, but | |
| a contradictory legacy/new pair is rejected. Family IDs and authority labels | |
| come only from the committed `benchmarks/sources.json` ontology. Selecting an | |
| empty family such as Patristics currently yields no official results; it never | |
| falls back to an unfiltered corpus search. Retrieved official entries are | |
| labelled as verbatim source text, curated source notes, or registry metadata. | |
| Neither the answering model nor a web-review model may assign official status, | |
| orthodoxy, imprimatur, dogmatic rank, or doctrinal authority. | |
| Web search is default-off and available only for the Catholic `llm-synod` | |
| browser workspace when the existing OpenRouter secret is configured. After the | |
| browser request reserves quota, ordinary Answer mode makes one fixed Exa-backed | |
| search using only a whitespace-normalized, 2,000-character maximum copy of the | |
| current message. Task mode may make up to four such searches, with bounded | |
| queries derived from the request and prior Task observations. Neither mode adds | |
| history, individual or organizational memory, attachments, uploaded filenames | |
| or content, file/citation locations, or device/geolocation context to a search. | |
| The controller cannot select arbitrary provider tools or fetch arbitrary URLs or | |
| files. Each search accepts at most five results and at most 2,000 characters per | |
| result; the final deduplicated web-evidence set for the request is capped at | |
| five. Only documented `url_citation` annotations are parsed; assistant prose is | |
| ignored for source discovery. The server never follows those URLs. It rejects | |
| non-HTTPS, credential-bearing, private/internal/reserved-host, and | |
| signed/authenticated URLs, strips fragments, deduplicates URLs, and bounds all | |
| excerpts. | |
| When `web_source_review` is enabled (the default), a separate strict-JSON GLM | |
| pass reviews each result batch using only its minimized search query plus opaque | |
| citation ID, title, domain, and bounded excerpt. It filters relevance, evidence | |
| usefulness, source genre, spam, manipulation, and likely prompt injection. | |
| Missing, malformed, uncertain, or excluded decisions fail closed. Turning | |
| review off skips only this suitability pass; it does not weaken deterministic | |
| URL, annotation, size, or citation-key validation. | |
| Accepted web excerpts remain untrusted supplemental evidence and cannot | |
| override the official Catholic corpus. Answers may cite only exact issued | |
| `[[w1]]` through `[[w5]]` markers. Browser replies retain the legacy source and | |
| document fields and add a trust-separated `citation_manifest` with official, | |
| web, and uploaded-document entries. Web entries include bounded title, public | |
| URL, domain, excerpt, UTC access time, excerpt hash, and review status/reason; | |
| the search query, engine/model internals, credentials, and unbounded content are | |
| not returned. Discovery, optional review, and generation share one quota | |
| reservation and one usage event, including partial usage on a failed review. | |
| Web search and review remain Synderesis-funded when a customer OpenRouter key | |
| funds selected-tier Answer or Task controller/final-answer calls; such a request | |
| reports mixed funding. | |
| Web evidence is server-side request-scoped and is not written to server | |
| conversation history. Only the bounded public manifest for sources actually | |
| cited may remain in account-scoped browser `localStorage`; uploaded-document | |
| citations remain transient. OpenRouter and Exa receive the ordinary Answer | |
| query or a bounded Task query derived from the request and prior Task | |
| observations, so their retention is governed by the configured provider account | |
| and terms. They do not receive appended history, memory, files, or device | |
| context from the search feature. The deployment's explicit question/answer | |
| logging flags remain a separate retention choice. Research bundles advertise | |
| both official retrieval and web search as unavailable and make neither source | |
| call. | |
| When the configured backend is the tested LLM Synod path, bounded decoder | |
| validation allows PNG, JPEG, GIF, and WebP. Image data URLs go only to validated | |
| visual advisers. Text-only advisers and the final synthesis/review stages | |
| receive bounded text context, candidate drafts, and successful visual-witness | |
| summaries. Other backends report image input as unavailable and reject images | |
| rather than silently ignoring them. PDFs use text extraction, not OCR. | |
| ## GitHub automatic deployment | |
| `.github/workflows/deploy-huggingface-space.yml` runs for pushes to `main` and | |
| from the **Run workflow** manual control. Create a GitHub Actions environment | |
| named `production` and add its sole required secret, `HF_TOKEN`, with permission | |
| to update the existing Space. Concurrent runs are serialized and an active | |
| deployment is not cancelled. | |
| CI calls `scripts/deploy_huggingface_space.py --upload-only`. It builds and | |
| uploads the bundle from the checked-out commit, preflights `/data`, and ensures | |
| the existing state volume after upload. It never decrypts `.env.age`, uploads | |
| the source database, reconfigures Space variables or secrets, or generates or | |
| rotates bootstrap customer keys. | |
| For an application rollback, revert the commit on `main` or manually dispatch | |
| the workflow for the desired revision. Persistent `/data` contents are not | |
| rolled back. Disable the workflow in GitHub Actions to stop automatic deploys; | |
| removing the production environment's `HF_TOKEN` also makes runs fail before | |
| upload. | |
| Supported backend values are `llm-synod` and `tinker`; `llm-synod` is the default | |
| for the deployed Space. The Synderesis Spark LLM Synod route uses MiniMax M3 and | |
| DeepSeek V4 Pro as one parallel low-cost OpenRouter adviser wave. MiniMax is | |
| pinned to the cheapest current GMICloud endpoint, which was verified for visible | |
| JSON and image output. DeepSeek is pinned to StreamLake and omits `seed`, which | |
| that endpoint does not support; the OpenRouter account's privacy guardrails | |
| continue to exclude the cheaper first-party DeepSeek route. Qwen3.7 Max is the | |
| first quorum rescue, with low-reasoning Grok 4.5 reserved for a failed Qwen | |
| rescue, two failed primaries, or missing visual evidence. Unavoidably paired | |
| Qwen and Grok rescues start in parallel. Non-reasoning GLM 5.2 is pinned to | |
| CoreWeave and is the final source-constrained Catholic | |
| synthesizer/gatekeeper. Muse Spark 1.1 is excluded because its OpenRouter route | |
| is restricted to US users. The 2026-07-28 review retained only endpoints that | |
| passed bounded live compatibility checks; advertised routes that returned | |
| 400/404 were rejected. Responses report whether escalation was used, while | |
| quota checks reserve four adviser calls plus synthesis, repair, and up to two | |
| semantic grounding-verifier calls. Routing keeps parameter support mandatory. | |
| Qwen and Grok use soft latency/throughput preferences without replacing the | |
| default price-aware ordering. It does not route GPT, Claude, or Gemini models | |
| through OpenRouter. After exact citation-pair validation, a semantic verifier | |
| rejects any answer or qualification that contradicts a controlling source note, | |
| reverses its teaching, attributes unsupported content to it, or overstates what | |
| it settles. The verifier accepts only server-resolved curated notes or exact | |
| official document text; registry-only metadata and user-supplied text are not | |
| verification evidence. One repaired answer is verified again. Signed-in browser | |
| chat, paid `/v1/answers`, and public `/v1/demo/answers` return the latest | |
| schema-valid, structurally safe answer with advisory metadata when semantic | |
| review remains rejected or retrieved official references go uncited. Malformed | |
| or schema-invalid output, missing citation identifiers, and invented or | |
| mismatched exact official citation pairs remain withheld. Every request retains | |
| request/token metering, quota accounting, and private provider-cost accounting | |
| for all candidate, synthesizer, repair, and verifier tokens already spent. | |
| Stripe overage is enqueued only for a final delivered `accepted` or `advisory` | |
| gatekeeper; withheld, missing, unknown, and failed outcomes remain nonbillable. | |
| Consumers should use the gatekeeper status and | |
| `grounding_status.delivery_status` as the release decision; strict diagnostic | |
| fields remain false for advisory cases. | |
| The uploaded variant is mutually exclusive and its bundle metadata is | |
| authoritative for both UI and server mode: a Catholic bundle rejects a | |
| caller-supplied research workspace, while a research bundle rejects Catholic | |
| chat, before quota or provider calls. | |
| Research requests call canonical `gpt-5.6-sol` at the direct OpenAI Responses API | |
| with validated image inputs, high reasoning, bounded output, and `store: false`; | |
| canonical `claude-opus-5` then synthesizes strict JSON at the direct Anthropic | |
| Messages API and may make exactly one repair call. Both hosts and model IDs are | |
| pinned, and GPT/Claude are never sent through OpenRouter. | |
| Missing `SYNDERESIS_RESEARCH_OPENAI_API_KEY` or | |
| `SYNDERESIS_RESEARCH_ANTHROPIC_API_KEY` fails only enabled research requests; | |
| the generic provider names remain accepted as local input aliases. A regular | |
| `--website-variant research` deploy that synchronizes secrets requires both keys | |
| from process memory or decrypted `.env.age` and stores the scoped names only as | |
| Space secrets. It does not require or install `OPENROUTER_API_KEY`. Catholic | |
| deploys and `--no-set-secrets` never resolve, update, or clear the direct keys; | |
| therefore research `--upload-only`/`--no-set-secrets` requires both | |
| direct-provider secrets to be preinstalled (scoped names preferred; generic | |
| aliases accepted). The public composite model is reported as | |
| `synderesis-v3-research-sol-opus`. Token usage and published model-price estimates | |
| from both direct providers—including a completed paid stage before a later | |
| provider failure—enter the existing quota contract. Exact uploaded-document | |
| `[[dN]]` validation remains unchanged. | |
| Canonical model routes belong only in Hugging Face Space variables. Every | |
| regular secret-synchronizing deploy removes all known model-route names from the | |
| Space secret namespace, including active route names and stale HF/Tinker | |
| aliases, so a misplaced secret cannot shadow the authoritative variable. | |
| The launcher supplies its built-in Tinker model path only when the effective | |
| backend is `tinker`; an explicit path remains untouched so wrong-backend drift | |
| still fails closed during runtime validation. Full non-Tinker secret sync also | |
| removes a stale `TINKER_API_KEY`, while Tinker sync installs it and every backend | |
| retains `HF_TOKEN` for Hub dataset access. | |
| `--no-set-secrets` and `--upload-only` leave the complete secret namespace | |
| unchanged. | |
| The website links a structured-answer citation only when its exact source ID and | |
| location match a retrieved passage. Matched links use human-readable source titles | |
| in separated list items. Citation navigation shows document-chunk text as the | |
| relevant passage and separately labels any summary. Curated retrieval-note rows | |
| are labelled as notes and are not presented as quotations. Registry-only rows | |
| are labelled as metadata rather than evidence. The renderer omits | |
| non-HTTP(S) or non-official external links. | |
| ## Website variant preparation | |
| The existing Catholic product remains the default compatibility mode: | |
| ```bash | |
| python scripts/deploy_huggingface_space.py --prepare-only --website-variant catholic | |
| ``` | |
| Prepare the replacement-ready research landing page and workspace overlay with: | |
| ```bash | |
| python scripts/deploy_huggingface_space.py --prepare-only --website-variant research | |
| ``` | |
| Both variants target the existing Hugging Face Space, so deployment retains the | |
| `https://www.synderesis.eu` canonical public origin. The research selection copies the complete shared | |
| website and replaces only its landing and chat HTML; the controller, styles, | |
| account/session gate, static assets, and citation renderer remain shared. | |
| Rollback consists of rebuilding and deploying with `--website-variant catholic`. | |
| Uploaded files, filenames, extracted text, and document-citation metadata remain | |
| request-scoped and are not stored in browser history. This thin variant adds no | |
| database, background job system, provider stack, or online literature-search | |
| capability. It does not deploy either variant by itself. | |
| # Optional Stripe billing | |
| When enabled, set the Stripe secret key and webhook secret as Space secrets. Set both price IDs, meter event name, public origin, included retail micro-USD, matching fixed monthly cents, expected livemode, and `SYNDERESIS_STRIPE_REQUIRE_PERSISTENT_LEDGER=true` as Space variables. `SYNDERESIS_STRIPE_BYOK_PLATFORM_FEE_RATE` is an optional exact-decimal Space variable that defaults to `0.25`; it is rejected when orphaned from the complete Stripe configuration. When the authoritative encrypted configuration omits it, a regular deploy replaces any stale custom value with the explicit `0.25` default. Any live-mode API ledger, including direct runtime configuration, must resolve below `/data`; startup fails rather than silently using ephemeral storage. The Stripe client is pinned to `2026-06-24.dahlia`, and startup validates the complete two-price catalog and Meter mapping. Stripe billing is supported only with the `llm-synod` backend because it is the only backend with a trusted provider-cost ledger; deployment rejects complete Stripe wiring for every other backend before making remote changes. | |
| Keep `SYNDERESIS_SERVICE_LIVE` false for normal private-Beta deploys even when Stripe is configured. A paid entitlement may open browser chat, but it never opens API-key management or device/extension routes; invitation registration and signed-in claims, waitlist membership, administrator endpoints, and complimentary nonmetered Beta access continue unchanged. Runtime managed retail uses the fixed true 50% provider-cost contribution margin. BYOK uses zero Synderesis provider cost plus the configured platform fee on trusted provider-reported cost, or on exact committed price multiplied by provider-reported tokens when cost is absent; it never uses character estimates, unknown-model prices, or the 1.055 loading. The tier-specific managed alternatives in `docs/pricing-margin-analysis.md` are recommendations only. | |