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. 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. 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. 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.