Local Contexts
Overview
Local Contexts Labels let Indigenous communities express their own rules for how knowledge about them may be used — provenance, permission, and protocol expressed by the community itself rather than inferred by a repository.
DOCiD integrates with the Local Contexts Hub. It does not author Labels and does not own them: the community does, through the Hub. DOCiD stores a reference plus a cached copy of the display metadata, so a record can render its Labels without depending on the Hub being reachable at that moment.
The Hub is authoritative
Every Label shown in DOCiD is a cached copy of Hub data. Where the two disagree, the Hub is correct and DOCiD's copy is stale. Labels can be changed or withdrawn by the community that issued them, and DOCiD must reflect that — which is why the cache expires rather than persisting indefinitely.
The conceptual background — what TK Labels mean and how to act on them — is in Cultural Protocols. This page documents the implementation.
The three context types
context_type |
Meaning |
|---|---|
TK_LABEL |
Traditional Knowledge Label — community expectations for using knowledge held by that community |
BC_LABEL |
Biocultural Label — applied to biological and genetic resources and associated knowledge |
NOTICE |
Notice — asserted by an institution rather than a community, such as Open to Collaborate |
The distinction matters: Labels are authored by communities; Notices are authored by institutions. A Notice is an institution stating its intent, not a community granting permission. Do not present the two as equivalent in an interface.
Storage model
Three tables, mirroring the separation between what the Hub owns and what DOCiD records.
local_contexts — the cache
| Column | Type | Notes |
|---|---|---|
external_id | String(255), NOT NULL, unique, indexed | The Hub's identifier — the join key to the outside world |
context_type | String(50), NOT NULL | One of the three values above |
title, summary | String(255), Text | Cached verbatim from the Hub |
community_name | String(255) | The community asserting the Label |
source_url, image_url | Text | Hub links and Label artwork |
is_active | Boolean, default true | Cleared when a Label is withdrawn upstream |
is_authoritative, needs_review | Boolean, default false | Declared but never set by the backend — see Known gaps |
cached_at, last_sync_attempt, sync_error | DateTime, DateTime, Text | Cache freshness and last failure |
publication_local_contexts — the attachment
Joins a publication to a cached Label, optionally recording the Hub project the Label came from, plus display_order, attached_at and attached_by.
Two partial unique indexes, not one constraint
Uniqueness is enforced by a pair of PostgreSQL partial indexes: one over (publication, label) WHERE project_external_id IS NULL, and one over (publication, label, project) WHERE project_external_id IS NOT NULL. The effect is that the same Label may be attached under two different projects, but never twice within the same project — and a legacy attachment with no project cannot be duplicated either. Both are Postgres-specific.
local_context_audit_log
Every change is recorded with an action of ATTACH, DETACH, PROJECT_ATTACH, SYNC or MARK_INACTIVE, alongside the publication, the label, the external id, the acting user, a JSON detail payload, the IP address, and a timestamp. Attaching a cultural Label to a record is a consequential act, and the log exists so it can be reconstructed later.
Caching and freshness
Cached Label metadata expires after 30 days. When a stale entry is read, DOCiD attempts a blocking resync against the Hub with a 10-second timeout, and falls back to the stale copy if the Hub does not answer in time — a slow Hub degrades freshness, never availability.
Where a resync cannot be attempted at all (for example, no Hub API key is configured), the response carries _cache_stale: true and a _cache_note explaining why. Clients should surface that rather than silently presenting month-old data as current.
Project display re-fetches the Hub in bulk and falls back to the cached shape per project, so one unreachable project does not blank the others.
Endpoints
All under /api/v1/localcontexts.
| Method & path | Auth | Purpose |
|---|---|---|
GET /projects | None | List Hub projects |
GET /projects/search | None | Search Hub projects |
GET /projects/<project_id> | None | One project with its Labels |
GET /projects/multi/<ids> | None | Bulk fetch several projects |
GET /projects/multi/date-modified/<ids> | None | Modification dates only — cheap staleness check |
GET /notices/open-to-collaborate | None | The Open to Collaborate Notice |
GET /publications/<id>/contexts | None | Labels attached to a publication |
POST /publications/<id>/contexts | JWT | Attach a single Label |
DELETE /publications/<id>/contexts/<context_id> | JWT | Detach a Label |
POST /publications/<id>/projects | JWT | Attach every Label of a Hub project at once |
DELETE /publications/<id>/projects/<external_id> | JWT | Detach a whole project |
GET /publications/<id>/projects-display | None | Display-ready project view with cache fallback |
GET /cache/<external_id> | None | Inspect one cached entry |
POST /cache/<external_id>/sync | JWT | Force a resync from the Hub |
GET /audit-log | JWT | Attachment history |
GET /health, GET /api-info | None | Integration status |
Attaching a project
The project attach endpoint is the one most integrations want: it attaches every TK Label, BC Label and Notice belonging to a Hub project in a single savepoint, using INSERT ... ON CONFLICT DO NOTHING so a partial re-attach is harmless. It returns the number attached and the project title. Label body text is taken from the first available of the Hub's label_text, default_text, or description fields.
Known gaps
Local Contexts data is absent from publication responses
Neither get-publication nor either DOCiD resolver nor the DOI resolver includes attached Labels. A client rendering a record must call /publications/<id>/contexts or /projects-display separately. A consumer that reads only the publication payload will not know a record carries cultural Labels at all — which is the failure mode Labels exist to prevent. Treat this as required reading for anyone building a display surface.
is_authoritative and needs_review are never set
Both columns exist and default to false, and no backend code sets either to true. Do not branch on them; they carry no meaning today.
PostgreSQL-only
The partial unique indexes and the conflict-tolerant bulk attach are Postgres-specific. This subsystem will not behave correctly on another database engine.