Submission lands
A kernel client (POST /api/submit/knowledge) or a curator opens a proposal. PII is regex-redacted client-side; the hub re-runs the same redactor server-side before write.
The hub is a small, public, append-only service. It signs and serves context packs, holds the global registry of grep rules and tools, accepts anonymized opt-in signals from partner deployments, and exposes a live audit feed. It does not see worker messages, names, or case content; those stay on the partner side.
Every knowledge pack moves through five stages before clients pull it as vetted. Unvetted (proposed / community-submitted) packs remain downloadable for researchers and integration partners who explicitly opt in — pre-anonymized, with clear status flags so a recipient never confuses a community draft with a curator-approved release.
A kernel client (POST /api/submit/knowledge) or a curator opens a proposal. PII is regex-redacted client-side; the hub re-runs the same redactor server-side before write.
Automated: each KnowledgeObject must validate (schema_version: "1.0", recognized knowledge_object_type, kebab-case id, well-formed content). Failures bounce with reasons.
Server-side regex + entity pass for PERSON / EMAIL / PHONE / AMOUNT / ID / EMPLOYER. Any detection blocks the proposal until the submitter re-anonymizes; the audit log records a sha256 of the offending fragment, never the raw value.
A curator reads the proposal end-to-end, checks citations, verifies corridor scope, and either accepts, requests changes, or rejects with a reason. Decisions are recorded as JSONL entries with curator key + timestamp + decision.
Curator signs the content hash. The pack is published with status vetted. Default client pulls from /api/hub/knowledge/download serve only vetted entries; clients can opt in to unvetted with ?vetted=false for research / integration.
Vetting is human-driven today and we say so publicly. An NGO caseworker or compliance reviewer should know exactly which stage their submission is at, who the curators are, and how to reproduce a decision. Automating Stage 04 with model-assisted review is the planned upgrade; nothing about the policy changes — only the cycle time.
Anonymized aggregate signals (Stage 01 & 02 outputs) flow into /stats so researchers can observe what the system sees without ever touching raw cases.
The hub holds public source material, vetted pack manifests, the registry of grep rules and tools, and an anonymized trend rollup. It is intentionally narrow: anything that could identify a worker or case is rejected at the schema boundary before it ever touches storage.
If a feature can't be expressed as one of these three jobs, it does not belong on the hub. New behavior moves into the harness or stays in the partner deployment instead.
Versioned bundles of public source material (laws, advisories, embassy notices) scoped to a corridor and frozen at a known date.
The grep rules every harness deployment runs, plus the catalog of tools deployments may call. One source of truth, mirrored on pull.
Partner deployments may post anonymized pattern counts (e.g.
fee_request per corridor, per week) so curators can see trends and
regulators can act on them.
Four things any deployment can rely on. Read access is open and unauthenticated; everything you pull is curator-vetted, every version stays available, and nothing about a worker is ever stored. Implementation details live in /docs.
Pull a knowledge pack
Download any version of any corridor pack as a single file. Pin it by version, verify it against the public curator key, ship updates on your own schedule.
duecare pull phl-kwt-domestic@1.7.2
Send an anonymized usage signal
Tell the hub which pattern fired and in which corridor, after your local anonymizer redacts everything else. Used for trends and research, never to identify a worker.
k-anonymity ≥ 30. Aggregate only. No times, no contacts, no text.
Verify any release
Every pack and rule carries a public signature. Run one command and your client tells you whether the file came from the curator or someone in the middle.
duecare verify pack.json
Replay any decision
Every version of every pack stays available forever. Pin a release by hash, point a fresh runtime at it, get the same answer the harness gave six months ago.
No deletes, no overwrites. Corrections ship as new versions.
Public audit feed
Every write produces an audit row exposed on a public feed. Anyone can replay the feed and reproduce the current state of the hub.
/audit/stream.ndjson
Most integrations are read-only: pull a pack, verify it, run locally. Partners who want to contribute trend data add one extra step: share an anonymized usage signal so the field can study which patterns are firing where.
Resolve the latest version of any corridor pack by id. The body is JSON; mirror it locally and your runtime stays offline-capable.
# latest version of the PHL→KWT corridor pack
curl https://gemma4-comp.onrender.com/api/hub/packs/phl-kwt-domestic
Combine any of kind, jurisdiction, corridor, tag, status_. The response also lists the discoverable values for each filter so a UI can populate dropdowns.
# every grep-rule pack vetted for any jurisdiction curl "https://gemma4-comp.onrender.com/api/hub/packs?kind=GrepRulePack&status_=vetted" # every pack tagged 'fees' for the Philippines curl "https://gemma4-comp.onrender.com/api/hub/packs?tag=fees&jurisdiction=PHL"
List every known version, then pin the one you want. The pinned URL is stable forever; nothing is overwritten.
# every version of a pack curl https://gemma4-comp.onrender.com/api/hub/packs/phl-kwt-domestic/versions # pull a specific version curl https://gemma4-comp.onrender.com/api/hub/packs/phl-kwt-domestic/1.7.2
Tell the hub the timestamp of your last successful sync. The response lists every vetted pack vetted after that and gives you the next cursor.
# first sync (no cursor): list every vetted pack curl https://gemma4-comp.onrender.com/api/hub/sync # subsequent sync: pass the next_cursor from the previous response curl "https://gemma4-comp.onrender.com/api/hub/sync?since=2026-04-12T09:14:00%2B00:00"
Recommended: poll every 6 hours from a cron and atomic-swap the pack file on disk only when the hash differs.
Aggregate counts only. Free text, identifiers, and case content are rejected at the schema boundary. The hub re-checks for PII even after your local anonymizer runs.
curl -X POST https://gemma4-comp.onrender.com/api/hub/signals \
-H 'content-type: application/json' \
-d '{
"source": "ngo_case_intake",
"jurisdiction": "Hong Kong",
"corridor": "PH-HK domestic work",
"risk_tags": ["recruitment_fee", "passport_retention"],
"summary": "Aggregate pattern: high recruitment fees plus passport-retention requests in this corridor this month.",
"consent_basis": "explicit_opt_in"
}'
Three levels of opt-out, from least to most restrictive. None of them require you to call the hub; you control the runtime config.
signals.disabled_corridors = ["PHL-KWT"] in your runtime config. Anything matching that corridor is never proposed for sending.signals.enabled = false in your runtime config. The hub never receives a signal from this deployment, period. Pulls keep working.There is no "force send" override. Local config wins.
Same endpoint the website's contribute form posts to. The server-side automation triages the summary; the curator queue is shared. kind picks the proposal type.
curl -X POST https://gemma4-comp.onrender.com/api/hub/client/submission \
-H 'content-type: application/json' \
-d '{
"kind": "context",
"deployment_id": "ngo-helpdesk-bd",
"organization": "Migrant Forum BD",
"jurisdiction": "BGD",
"corridor": "BGD-SAU",
"public_source_url": "https://example.gov/bd-sau-advisory-2026",
"summary": "New regulator advisory clarifies the placement-fee cap for the BGD-SAU domestic corridor.",
"consent_public_proposal": true
}'
Only succeeds while the submission is still proposed or needs_review. Once a curator approves or rejects, the record is immutable.
curl -X POST https://gemma4-comp.onrender.com/api/hub/client/submission/retract \
-H 'content-type: application/json' \
-d '{
"submission_id": "cli_abc123def456",
"reason": "operator changed their mind after submitting"
}'
These are not values; they are constraints in code. If a pull request tries to add behavior that breaks one of them, the hub does not ship it.
Every pack input is a public document with a citable URL. Curator review is recorded on the diff that brings it in.
The JSONL append-only store has no name, message, contact, ID, or employer column. There is nothing for a misconfigured client to populate.
Every pack, rule, and tool keeps every prior version. Corrections ship as new versions; the old ones stay available so a six-month-old advisory can still be reproduced.
Trend slices below k≥30 are not published. The floor is applied before any aggregate leaves the storage layer.
Signing keys rotate on a fixed schedule. Old keys are revoked but remain in the public log so historical packs stay verifiable.
Every change shows up in the public history feed before anyone can read the result. Pin a release to a hash, point a fresh runtime at it, get the exact same answer the harness gave at the time.
Start with the API client guide for pulling packs and verifying signatures, or read the safe-use checklist for partner deployments.