# Glossary
Source: https://docs.privacytracker.privacykey.org/glossary

Domain terms used across privacytracker — what they mean and where they appear in the code.

A short reference for terms that show up across the app, the API, and the codebase. If you've just landed in `lib/`, this page is the fastest way to read prose around it.

## Core data

**App.** A single tracked iOS application. The primary key (`apps.id`) is the numeric Apple track ID extracted from `/id<digits>/` in the App Store URL — *not* a UUID. Snapshots, privacy rows, and notifications all key off this.

**Privacy type.** Apple's top-level grouping in the App Privacy section — *Data Used to Track You*, *Data Linked to You*, *Data Not Linked to You*, *Data Not Collected*. Stored in the `privacy_types` table.

**Privacy category.** A specific data category within a privacy type — *Location*, *Contact Info*, *Identifiers*, *Diagnostics*, etc. Stored in `privacy_categories`, foreign-keyed to `privacy_types`.

**Severity tier.** Our colour-coded grouping of categories by sensitivity — `high` (precise location, sensitive contacts, financial info), `medium` (coarse location, contacts, search history), `low` (diagnostics, identifiers). Defined in `lib/privacy-meta.ts`'s `SEVERITY_CONFIG`.

**Bundle ID.** Apple's reverse-DNS identifier for an app (`com.spotify.client`). Stored on `apps.bundleId`. Cosmetic — joins are by track ID.

## Scrape and parse

**Shelf.** Apple's name for a row of content on the App Store page. The privacy section is a shelf with `id="privacy"`. The parser walks `data[0].data.shelfMapping.privacyTypes.items` first, then falls back to two older nesting layouts, then finally to the historical Ember/FastBoot **shoebox** shape. Apple's HTML has changed shape several times; the fallback chain absorbs that.

**Serialized server data.** The JSON payload Apple embeds in `<script id="serialized-server-data">` on every modern App Store page (introduced with the Nov 2025 redesign). Wraps as `{ data: [...], userTokenHash }` — always unwrap via `Array.isArray(raw) ? raw : raw.data`.

**Shoebox.** The pre-redesign Ember/FastBoot pattern Apple used from Jan 2021 through Nov 2025. Privacy data lives inside `<script type="fastboot/shoebox" id="shoebox-media-api-cache-apps">`, in a JSON object keyed by Apple API request URLs whose values are JSON-encoded strings. Path to privacy: `d[0].attributes.privacy.privacyTypes` (note: `d` not `data`, direct array not `{items}`, and `dataCategories`/`dataCategory` instead of `categories`/`title`). The identifier enums are unchanged from the modern shape, so downstream snapshot diffing and severity styling work without translation. `extractFromShoebox` in `lib/scraper.ts` handles the rename. This is what lets the Wayback importer reach back to Q1 2021.

**Manual app.** An app you tracked manually rather than via App Store scrape — e.g., for a regional app not available in your storefront. Stored in `manual_apps`, with its own scrape pipeline that doesn't talk to the iTunes Search API.

## Snapshots and changes

**Snapshot.** A frozen `PrivacyTypeSnapshot[]` for one app at one moment in time. Stored as JSON in `privacy_snapshots.snapshot_json`. Built from the database *after* a write completes (see `buildSnapshot()` in `lib/changelog.ts`).

**Diff.** The human-readable change list between two snapshots (added/removed categories, severity shifts). Stored alongside the snapshot in `privacy_snapshots.changes_json`.

**Change count.** Per-app counter of unacknowledged change events (`apps.changeCount`). Drives the bell badge. Decremented when the user acknowledges via `POST /api/apps/[id]/acknowledge`. Wayback imports skip the bump (back-dating shouldn't inflate the badge).

**Trigger pill.** The little label on each timeline row showing what kicked off the snapshot — `scheduled`, `manual`, `import`, `wayback`, or `null` (legacy rows pre-migration). Driven by `privacy_snapshots.triggered_by`.

**Matches live sync.** Green badge on a Wayback snapshot when its byte-identical content matches an adjacent live row. Means the archive and the live App Store page agree at that point in time. Computed in `getChangelog()` and surfaced via `matches_live_sync: true` on the row.

## AI and policy

**Policy version.** A captured version of a developer's privacy policy (URL, fetched text, SHA-256 hash, AI summary if generated). Stored in `policy_versions`. Re-summarisation only runs when the hash changes.

**Policy event.** What a privacy-policy fetch records on the app's History timeline: `first` (the first usable capture), `same` (unchanged text), `changed` (new text, diffable against the previous version), or `error` (a failed or unusable fetch). Every fetch records one, as `policy_event` in a `privacy_snapshots` row's change summary. Only `changed` can be flagged for review or raise a notification, and only while `flag.notifications.types.policy_updates` is on. See [Configuration → Notifications](https://docs.privacytracker.privacykey.org/configuration#notifications).

**Lens.** One topic area an AI summary covers. Eight of them, in this order: `collection_scope`, `product_use`, `ads_marketing`, `third_party_sharing`, `tracking_analytics`, `user_controls`, `data_retention`, `children_minors`. Defined in `POLICY_LENSES` in `lib/policy-summary-meta.ts`; the per-lens prompt guidance lives in `POLICY_TOPIC_GUIDES` in `lib/privacy-policy.ts`. Summaries are rebuilt against that list, and keys outside it are discarded — so `summary_json` and the API only ever carry these eight.

**Chunking.** Splitting a long policy into ~12 KB pieces for small/local models. Triggered by the `providerLikelyNeedsChunking` flag in `lib/ai-config.ts`. Per-lens prompts are generated against each chunk and merged.

**AI debug log.** Append-only diagnostic table showing every AI call's provider, model, prompt size, response status, and timing. Off unless *Record AI prompts and responses* is enabled. Inspectable from **Settings → Admin → Developer Options** or `GET /api/ai/debug-log`.

## Focus and feature flags

**Focus.** The user's combined choice of *audience* and *goals*. Drives every default in the feature-flag resolver. Stored as seven `app_settings` rows: `flag.focus.audience`, four `flag.focus.goal.*` booleans, `flag.focus.workflow`, and `flag.focus.updated_at`.

**Audience.** One of `self`, `loved_one`, `guardian`. Determines moderate-weight defaults across the resolver.

**Goal.** Mutually exclusive primary goal (`monitor`, `cleanup`, `minimal`) plus an optional `accessibility` modifier. The modifier combines with whichever primary goal is active. `monitor` and `cleanup` were formerly called `understand` and `declutter`; the boot migration moves the old `app_settings` rows onto the new keys.

**Workflow.** The sixth of those rows, `flag.focus.workflow` — one of `self_monitor`, `self_cleanup`, `other_handoff`, `other_monitor`, `custom`. Inferred from audience + goals where that's unambiguous, `custom` otherwise. Defined in `lib/focus-workflow.ts`; its one behavioural effect today is that `other_handoff` opens audit-bundle export — one of the two ways that gate can be satisfied.

**Hard default.** The value a flag falls back to before any rules apply. Defined in `HARD_DEFAULTS` in `lib/feature-flag-rules.ts`. The kill-switch (`flag.devopts.feature_flag_system.enabled = off`) collapses every flag to its hard default.

**Override.** An explicit per-flag value set by the user in **Settings → Developer Options → Feature flags**. Stored in `feature_flag_overrides`. Always the final word in the resolution order.

**Quarantined.** An override row whose `flag_key` isn't in the current version's `FlagKey` union. Set during the boot-time quarantine check; reactivated automatically if the key is reintroduced. Means a backup from a future version restored cleanly without polluting the resolver.

## Bulk runners

**Bulk runner.** One of three crash-safe loops that operates over many apps in series — *App Store sync* (`lib/sync-bulk-runner.ts`), *Wayback import* (`lib/wayback-bulk-runner.ts`), or *policy sync* (`lib/policy-bulk-runner.ts`). All three persist their queue and per-app state at every app boundary, so a process kill loses at most one app's worth of work.

**Mutex.** The cross-request lock that prevents two runs of the same job from overlapping. Stored in `app_settings` under `sync_running`, `wayback_import_running`, and `policy_sync_running`.

**State blob.** The JSON document persisting a runner's queue, totals, and `initiator` metadata. Stored in `app_settings` under `sync_bulk_state`, `wayback_bulk_state`, and `policy_bulk_state`. Survives a process kill; cleared on clean completion.

**Initiator.** Where a run came from — `manual` (user clicked a button), `scheduled` (the 30-minute ticker), or `resume` (boot-time auto-resume after a crash). Surfaces in the UI as a "Resumed after restart" pill on resume runs.

**Save Page Now (SPN).** archive.org's API for requesting a fresh capture of a URL. The Wayback importer fires this fire-and-forget when a quarter has no usable capture, so a future run can pick it up.

## Notes and verdicts

**Annotation.** A freeform per-app note. Supports markdown, soft-delete with 30-second undo, tags (`concern` / `positive` / `follow_up` / `other`), and per-note visibility (`export` / `private`). Stored in `annotations`.

**Verdict.** A structured per-app judgement — `safe`, `replace`, or `uninstall`, plus an optional `rationale`. Stored in `app_verdicts`, with a `CHECK` constraint on the three values. Distinct from annotations because verdicts are categorical and exportable in the audit bundle.

**Audit bundle.** A versioned export of apps, labels, AI summaries, and exportable annotations + verdicts, suitable for sharing with a household member or a regulator. `POST /api/export/audit-bundle` allows it when the `flag.settings.admin.export.audit_bundle` flag resolves to `on`, or when `flag.focus.workflow` is `other_handoff`. The `loved_one` audience rule sets that flag to `on`, so picking that audience satisfies the first arm on its own; `self` and `guardian` need the `other_handoff` workflow or an explicit override. Private notes (`visibility = 'private'`) are unconditionally excluded by SQL filter — there is no force-include path.

**Shortlist.** A user-curated set of apps you want to keep an eye on without installing. Separate from tracked apps; you can shortlist an app you've never installed.

## Devices

**Device.** A phone or tablet you've imported apps from. Keyed on its ECID when the import carries one, so re-importing the same device updates its row. See [Devices](https://docs.privacytracker.privacykey.org/devices).

**Device menu.** The selector at the top of every page that decides which devices' apps privacytracker shows — one, several, or all. Every library read opts into it with a `devices` query parameter. A view, not an access boundary.

**Device owner.** Who a device belongs to — a free-text name plus `self`, `loved_one` or `guardian`. Never inferred. When recorded, the uninstall gate requires it to match your audience.

**Permission confirmation.** Your recorded statement that you have the owner's permission to view and act on someone else's device. Timestamped, written to the audit log, and required before privacytracker removes apps from that device.

## Distribution and runtime

**Tauri.** The Rust framework that wraps the Next.js bundle as a signed, notarized desktop app on macOS. Uses Tauri's updater with ed25519 signature verification.

**Sidecar.** The Node process that runs the server inside the desktop app on releases before the Rust backend: the same Next.js standalone bundle that Docker images before the Rust server run, launched as a child process by the Rust shell. From the first release on the Rust backend the server is compiled into the app, and no Node ships with it. See [Tauri & the backend](https://docs.privacytracker.privacykey.org/develop/tauri).

**Standalone build.** The output of `npm run build:standalone` (with `BUILD_STANDALONE=1` set). Self-contained Next.js bundle suitable for the sidecar or any external runtime.

**Admin token.** Optional shared secret (`AUDITOR_ADMIN_TOKEN`) that gates destructive routes when set, plus a list of sensitive read prefixes once the instance is network-exposed. Sent as the `X-Auditor-Admin-Token` header or the `pt_admin_token` cookie. Verified with `crypto.timingSafeEqual`. Failed attempts log to `audit_log` as `admin_token.login.invalid`.
