API Reference
API Reference/PAC

PAC: the content bridge

org.poca.pac · PAC Reader owns the provider

One read-only ContentProvider re-serves everything on a community-curated microSD card to the whole suite. This page is the complete provider reference.

2 authorities signature permission read-only by design

Access model

Both authorities are exported but guarded by org.poca.pac.permission.READ_CARDS, a signature-level permission: only apps signed with the shared POCA key can query. Clients also declare provider visibility:

<!-- client AndroidManifest.xml -->
<uses-permission android:name="org.poca.pac.permission.READ_CARDS" />
<queries>
  <provider android:authorities="org.poca.pac.cards" />
</queries>

The surface is read-only: insert() and delete() throw, and update() accepts exactly one operation, the culture-wheel active-design override. Clients degrade gracefully when the provider is absent or the card is unmounted (empty cursors, null).

Endpoints: org.poca.pac.cards

Card content, PAC metadata, app configuration, and raw file access. /file/ and /files/ accept multi-segment paths and are routed by hand; everything else goes through the UriMatcher.

EndpointReturnsCursor columns
/cardsAll visible cards.CARD_COLUMNS (below)
/cards/{card_id}A single card by id.
/apps/{app_id}Cards for one app. Returns nothing unless the app is queryable under its provisioning tier.
/featuredFeatured cards from feed-visible apps, for dashboard and widget hydration.
/indexOne row of PAC metadata.schema_version (Int), pac_version, tribe_name, locale, generated_at (String), total_cards (Int)
/config/{app_id}One row: the app’s provision plus its raw app_config.json. Disabled unlockable apps return nothing.app_id, directory, provision (core|bundled|unlockable), enabled (Int 1|0), config_json (String)
/file/{path}openFile() read-only stream of any file under the PAC root. Path-traversal guarded; mode must be r.n/a (stream)
/files/{dir}Directory listing cursor, sorted by name.name, path (String), is_dir (Int 1|0), size (Long)

Card cursor schema (CARD_COLUMNS)

Every card endpoint returns these 20 columns. Strings unless noted.

ColumnNotes
card_idUnique id within the PAC.
app_idOwning app’s lowercase slug (see directory map below).
typePer-app content type. Plain string; each app defines and routes on its own vocabulary.
title / descriptionPrimary and secondary display text.
image / image_thumbnail / image_heroPAC-relative media paths (nullable).
iconMaterial symbol name or SVG path (nullable).
labelPill label text (nullable).
deep_linkpoca://{app_id}/{type}/{identifier} target.
localeContent locale, default en.
updated_atISO 8601 timestamp.
featuredInt 1|0. Dashboard hint.
schema_versionInt. Forward-compat versioning.
extensions_jsonEvery non-universal key of the card’s JSON, re-serialized as one JSON object. This is how app-specific fields travel.
card_json_pathPAC-relative path to the card’s own detail manifest (nullable).
content_dir_pathPAC-relative directory holding the card’s media (nullable).
content_collectionDerived from the directory tree (the path segment after the app’s directory), not from JSON. A grouping label, never a routing key.
image_uriReady-to-open content://org.poca.pac.cards/file/… URI for image. The only image field that works across app boundaries; null when image is blank.

Endpoints: org.poca.pac.identity

Serves the Gateway launcher’s culture wheel. Design listings return WHEEL_DESIGN_COLUMNS: id, name, wheel_pattern_uri, pattern_path, pattern_hash, active (Int 1|0).

EndpointReturns
/wheel/designsAll wheel designs on the PAC.
/wheel/designs/{id}/patternThe design’s pattern image, opened as a file stream.
/wheel/activeThe active design row. Writable: update() with ContentValues {"id": designId} sets a local override; the pattern must decode as a bitmap to be accepted.
/wheel/active/patternThe active design’s pattern image stream.

Consumers should open the image via the wheel_pattern_uri column rather than constructing pattern URIs by hand.

On-card files

A PAC is plain files. Four JSON documents drive everything; markdown code fences around JSON are tolerated and stripped.

poca_config.json (PAC root)

{
  "tribe_name": "Example Nation",      // default "Unknown"
  "locale": "en",                       // default "en"
  "pac_version": "1.2.0",               // default "1.0.0"
  "apps": [
    { "app_id": "amoxtli",             // unknown ids are skipped
      "directory": "stories",
      "provision": "core",              // core | bundled | unlockable
      "enabled": true }
  ],
  "ota": { /* optional update channel, see OTA below */ }
}

poca_cards_index.json (PAC root)

Index keys: schema_version (default 1), pac_version, tribe_name, locale, generated_at, cards[]. Each card entry uses the universal keys from the cursor schema above; any other key is captured into extensions and surfaces through extensions_json. When the index file is missing, PAC Reader walks every card.json under each app directory, builds the index, and caches it back to the card.

{app_dir}/app_config.json

Read as raw text and returned verbatim in the config_json column. The provider never parses it; each app defines its own payload (Metztli’s calendar block is the flagship example).

identity/wheel.json

{
  "activeDesign": "sun-spiral",         // or active_design; default: first entry
  "designs": [
    { "id": "sun-spiral",               // required
      "name": "Sun Spiral",
      "pattern": "identity/sun.webp",    // or pattern_path; required
      "pattern_hash": "…" }            // optional
  ]
}

Legacy filename identity/wheels.json is also accepted. Pattern files must live under identity/.

App directory map

Each app id maps to a fixed directory under the PAC root. Content collections are the subdirectories beneath it.

app_idPAC directoryapp_idPAC directory
amoxtlistories/tlallinature/
metztlicalendar/cuallihealth/
tlatolanguage/cuilocreativity/
ollinceremonies/nahualprimal/
teospiritual/cristobible/

The misspelling meztli is accepted and normalized to metztli.

Media and file access

/file/{path} resolves against the PAC root with a canonical-path guard (no traversal, regular readable files only) and serves these MIME types:

ExtensionMIME type
png / webp / jpg, jpeg / svgimage/png · image/webp · image/jpeg · image/svg+xml
mp3 / m4a, m4b / ogg / wav / flacaudio/mpeg · audio/mp4 · audio/ogg · audio/wav · audio/flac
json / epub / pdfapplication/json · application/epub+zip · application/pdf
anything elseapplication/octet-stream

Wheel patterns get a second guard: they must resolve under identity/. PAC Reader also drops a .nomedia marker so card media stays out of the system gallery; cross-app image loading should always go through image_uri.

Provisioning tiers

The PAC decides which apps exist for its holder. Two independent flags derive from the tier: queryable (can be listed via /apps and /cards) and feed-visible (appears in /featured).

Core

Always queryable. Feed-visible while enabled.

Bundled

Always queryable. Each person can toggle it; the toggle is stored on the device, not the card, and survives card updates.

Unlockable

Community-managed. Until enabled it is invisible everywhere: /apps returns nothing, /config returns nothing, as if the app weren’t there.

Cards missing a config entry default to visible. Local bundled toggles fire a change notification so every consumer refreshes.

Change notifications

Register a ContentObserver on content://org.poca.pac.cards with descendants enabled and invalidate caches on any change. The provider notifies:

Community-run updates (OTA)

Each community can run its own signed update channel; no central store, no third-party gatekeeper. The channel is declared in poca_config.json under ota: base_url, community_id, channel (default production), signing_public_key, optional enrollment_code.

HTTP endpointPurpose
POST /v1/device/enrollOne-time-code device enrollment; returns device credentials (stored encrypted on device).
GET /v1/device/channels/{channel}/latestLatest release id for the channel.
GET /v1/device/releases/{id}/manifestThe signed release manifest.
GET /v1/device/blobs/{sha256}Content blobs, resumable via HTTP Range.

Manifest (protocol_version 1): release_id, community_id, channel, key_id, min_pac_reader_version, files[] {path, sha256, size, content_type}, optional tombstones[] (deletions), signature. Verification: canonical sorted-key JSON signed with Ed25519; per-file SHA-256; duplicate and unsafe paths rejected (no absolute paths, no dot segments, .ota/ reserved). Files stage under .ota/{release}/staging and apply as a journaled transaction with rollback; poca_config.json and poca_cards_index.json apply last. The worker runs every 6 hours on unmetered connections, then reloads the reader and notifies observers.

Last updated: July 2026
BackAPI overview