Global Systemic Injustice Graph
What it is
Multiple independent ClearTrace instances opt-in to share graph metadata (not raw docs) to a global map service. The service aggregates nodes/edges and renders a live, explorable “systemic injustices” network.
Data shared (safe-by-default)
Nodes: entity_id_public, name, kind (org/person/project/case), country, sector, risk_grade, public_dossier_url. Edges: source_id_public, target_id_public, relation_type (subsidiary_of, funded_by, litigated_in, reported_by, partner_of), evidence_count_public, first_seen_at, last_verified_at. Flags: repeat_offender, under_investigation, verified_pct. No private evidence, PII, or unverified content is shared—only summaries + URLs to public dossiers.
Minimal API (per instance → hub)
POST /federation/export
Body: { nodes: [...], edges: [...], instance_signature, schema_version } Auth: signed JWT or mTLS; key scoped to “federation:write”. GET /federation/map
Returns merged graph (paged or by tile). GET /federation/instance/:id/health
Heartbeat & last-sync time. Identity & deduping
Each instance derives entity_id_public = sha256(instance_namespace + local_entity_uuid). Optional entity claims (e.g., legal name + country + registry number) allow the hub to probabilistically merge duplicates across instances. Conflict strategy: keep both nodes, add a “possible duplicate” edge; stewards can confirm merges. Governance & consent
Opt-in per instance with a toggle + policy acceptance. Publish a Federation Charter: what’s shared, how merges work, takedown & appeals, audit cadence. Reputation for instances (uptime, data quality, verified ratio) → used to weight map confidence. Privacy & safety
Strip PII, redact testimony, share only verified or closed case aggregates by default. Allow per-edge masking if a jurisdiction requires it. Rate limits + abuse detection on the hub. UI/UX (global map)
Filters: domain (ecological/social/political/economic/cultural), sector, geography, risk. Layers: entities, cases, relationships, time slider (evolution). Provenance chip: “From 5 instances • 34 verified items” → links to source dossiers. Hubs & repeat offenders: auto-highlight central nodes. Story paths: save & share curated walks (like Kumu “views”). MVP build (2–4 sprints)
Schema additions (local): public_entities.claims_json (legal_name, registry_no, iso_country). public_relationships.publicity_level (public/internal/redacted). entity_federation_opt_in (bool). SQL view federation_graph_v that outputs the safe node/edge payloads. Simple Node/Go service + Postgres/Neo4j. Validates signatures, version, and schema; stores nodes/edges with instance_id. Exact match on claims; fuzzy on (legal_name + country); else mark possible_duplicate. Cytoscape.js/D3 force layout, filters, search, provenance chips. Federation Charter + instance setup guide. Versioning & integrity
Include schema_version, exported_at, and a bundle hash of the payload. Hub keeps snapshot history for rollback & audits. Risks & mitigations
Conflicting claims: show both; require additional verification to merge. Jurisdictional constraints: per-edge redaction + instance-level geo policies. Spam/poisoning: instance allowlist, human steward review for new instances, anomaly detection. Roadmap
v2: Federated search API, multilingual labels, signed provenance attestations per edge. v3: Cross-instance alerts (when a repeat offender appears elsewhere), curated “systems lenses” (sector packs), and public embed for campaigns.
1) Tiny OpenAPI spec (YAML)
openapi: 3.0.3
info:
title: ClearTrace Federation Hub API
version: 0.1.0
description: >
Minimal endpoints for federated graph exchange across ClearTrace instances.
servers:
- url: https://hub.cleartrace.example
security:
- ApiKeyAuth: []
- BearerAuth: []
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
Node:
type: object
required: [id, label, kind]
properties:
id: { type: string, description: Public-safe id (instance-scoped hash) }
label: { type: string }
kind: { type: string, enum: [entity, person, project, case] }
country: { type: string, nullable: true }
sector: { type: string, nullable: true }
risk_grade: { type: string, enum: [low, medium, high, unknown], nullable: true }
dossier_url: { type: string, format: uri, nullable: true }
created_at: { type: string, format: date-time, nullable: true }
Edge:
type: object
required: [id, source, target, type]
properties:
id: { type: string }
source: { type: string }
target: { type: string }
type: { type: string, enum: [subsidiary_of, funded_by, partner_of, litigated_in, reported_by, related_to] }
evidence_count: { type: integer, minimum: 0, default: 0 }
first_seen_at: { type: string, format: date-time, nullable: true }
last_verified_at: { type: string, format: date-time, nullable: true }
GraphBundle:
type: object
properties:
nodes:
type: array
items: { $ref: '#/components/schemas/Node' }
edges:
type: array
items: { $ref: '#/components/schemas/Edge' }
ExportPayload:
type: object
required: [nodes, edges, instance_id, schema_version, exported_at, bundle_hash]
properties:
instance_id: { type: string, description: short id for the sending instance }
schema_version: { type: string, example: "0.1.0" }
exported_at: { type: string, format: date-time }
bundle_hash: { type: string, description: sha256 of canonicalized payload }
nodes:
type: array
items: { $ref: '#/components/schemas/Node' }
edges:
type: array
items: { $ref: '#/components/schemas/Edge' }
Health:
type: object
properties:
instance_id: { type: string }
last_exported_at: { type: string, format: date-time, nullable: true }
status: { type: string, enum: [ok, stale, unknown], default: ok }
paths:
/federation/map:
get:
summary: Get merged federated graph
security: [] # public-readable map; adjust if needed
parameters:
- in: query
name: since
schema: { type: string, format: date }
description: Filter edges by last_verified_at >= since
- in: query
name: sector
schema: { type: string }
- in: query
name: country
schema: { type: string }
- in: query
name: risk
schema: { type: string, enum: [low, medium, high, unknown] }
responses:
'200':
description: Merged graph
content:
application/json:
schema: { $ref: '#/components/schemas/GraphBundle' }
/federation/export:
post:
summary: Push a safe graph bundle from an instance to the hub
security:
- ApiKeyAuth: []
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/ExportPayload' }
responses:
'202':
description: Accepted for ingestion
/federation/instance/{instance_id}/health:
get:
summary: Get last-seen status for an instance
parameters:
- in: path
name: instance_id
required: true
schema: { type: string }
responses:
'200':
description: Health info
content:
application/json:
schema: { $ref: '#/components/schemas/Health' }