Local Contexts

Traditional Knowledge and Biocultural Labels, and Notices, attached to a DOCiD

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

ColumnTypeNotes
external_idString(255), NOT NULL, unique, indexedThe Hub's identifier — the join key to the outside world
context_typeString(50), NOT NULLOne of the three values above
title, summaryString(255), TextCached verbatim from the Hub
community_nameString(255)The community asserting the Label
source_url, image_urlTextHub links and Label artwork
is_activeBoolean, default trueCleared when a Label is withdrawn upstream
is_authoritative, needs_reviewBoolean, default falseDeclared but never set by the backend — see Known gaps
cached_at, last_sync_attempt, sync_errorDateTime, DateTime, TextCache 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 & pathAuthPurpose
GET /projectsNoneList Hub projects
GET /projects/searchNoneSearch Hub projects
GET /projects/<project_id>NoneOne project with its Labels
GET /projects/multi/<ids>NoneBulk fetch several projects
GET /projects/multi/date-modified/<ids>NoneModification dates only — cheap staleness check
GET /notices/open-to-collaborateNoneThe Open to Collaborate Notice
GET /publications/<id>/contextsNoneLabels attached to a publication
POST /publications/<id>/contextsJWTAttach a single Label
DELETE /publications/<id>/contexts/<context_id>JWTDetach a Label
POST /publications/<id>/projectsJWTAttach every Label of a Hub project at once
DELETE /publications/<id>/projects/<external_id>JWTDetach a whole project
GET /publications/<id>/projects-displayNoneDisplay-ready project view with cache fallback
GET /cache/<external_id>NoneInspect one cached entry
POST /cache/<external_id>/syncJWTForce a resync from the Hub
GET /audit-logJWTAttachment history
GET /health, GET /api-infoNoneIntegration 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.