Privacy

Last updated: 2026-07-25

Yarrow is built for quiet. We collect as little as possible, and we do not track you across apps, sites, or advertising networks.

What stays on your device

Your questions, your cast hexagrams, your journal entries, and your app preferences are stored locally on your device. The built-in reference library also lives in the app: the 64 hexagrams, trigram attributes, classical-source excerpts, related hexagrams, and Jiao Shi Yi Lin changing-hexagram notes. Casting, lookup, source reading, and journaling work offline, without a network connection.

On iPhone, Yarrow uses Apple's standard local storage (SwiftData and UserDefaults). On Android, Yarrow uses local app storage. If you use the web demo, the browser keeps only a random quota identifier in local storage. Your question text never leaves your device or browser unless you ask for an AI reading.

What goes to our server when you ask for an AI reading

Normal casting and classical-source lookup do not need our server. When you explicitly request an AI interpretation, the following is sent to our Cloudflare Worker at yarrow-oracle.shaozhengkun.workers.dev:

The Worker forwards this to the configured language-model API. The model's reply is sent back to your device or browser and is not stored on our servers.

What we do not collect

Aggregate usage counters

To understand whether Yarrow is useful and reliable, the server keeps short-lived aggregate counters for reference-page visits, guide visits, entry events, successful local Journal saves, saved-reading opens, and AI reading attempts, such as daily hexagram-reference visits, practice-guide visits, homepage visits, the ready-question page contract, ready-question edit/focus or own-question choices, successful classical-source previews, voluntary full-reflection choices, download-button clicks, Telegram-button clicks, share-button clicks, and reading success/error counts. A Journal event contains only the fixed event name and app/QA source; it does not contain your question, cast, reading, reflection note, saved-record ID, locale, campaign, or app version. Saving and reopening remain local-first and do not wait for this best-effort counter. One explicit ready-question edit plus its programmatic focus is stored as one event and reported as the two logical interaction states. Operational QA and health-source names are rejected before persistence and filtered again during reporting, including both underscore and hyphen forms. Native feature sources remain excluded from natural reports until the matching store version is publicly verified; reporting begins on the first full UTC day afterward so same-day pre-public tests cannot re-enter totals. Raw pre-public keys remain short-lived and expire rather than being rewritten or deleted. These counters do not include your question text, reading text, raw device UUID, or raw IP address.

For daily unique-entry, unique-reader, unique-Journal-save, and unique-saved-reading-open counts, the Worker uses a salted one-way hash of the local quota or browser identifier for that day. The headline daily entry and reader totals deduplicate their hashes across all acquisition sources. Journal-save and saved-reading-open hashes use separate scopes, so they cannot increase headline entry or reader totals. Source-attributed breakdowns may count the same daily hash once under each source. This lets us count approximate unique use without storing the identifier in usage metrics or linking one day to another.

For a successful non-automated AI reflection, the same short-lived event record may include one coarse completion-time bucket, such as under 8 seconds or 15–25 seconds. It never stores the exact elapsed time. Aggregate latency buckets, p50/p95 bounds, and error rates are withheld until the requested reporting window contains at least 20 eligible samples.

Automated Codex QA, live product audits marked with product_live_*, product QA sources marked with product_qa_* or qa_* prefixes, explicit smoke_* tests, Worker cron, and health-check traffic are excluded before these aggregate counters are persisted. Browser and native product surfaces use fixed, low-cardinality client labels when they request a reading through the MCP-compatible endpoint. Fallback, unknown, and recognized external MCP integration activity is reported separately and is never counted as a natural user reading; historical activity that cannot be attributed to a product surface also remains in that separate MCP class.

Structured product feedback

If you choose to send product feedback on the website or, where offered by your installed version, after a completed I Ching answer in the iPhone or Android app, the form sends only one fixed reason, the stage and product surface, your selected language, the feedback-form version, and a one-time random submission ID created for that answer. The form has no free-text field and does not send your divination question, classical-source text, AI reading, journal text, email, device identifier, page URL, campaign reference, user agent, or viewport.

A confirmed natural feedback record keeps the fixed category and reason, surface, language, version, receipt state, and backlog-triage fields for up to 180 days. A separate aggregate index keeps only those enumerated fields for about 35 days. Public receipt readback shows only the receipt ID, timestamps, and received state; aggregate readback contains counts only.

Native feedback without a browser Origin is accepted only when the fixed client label exactly matches the declared Android or iPhone app surface. Debug app clients and automated sources marked codex_*, product_live_*, product_qa_*, qa_*, smoke_*, health_*, or Worker cron are excluded before any feedback KV write. They receive an explicit QA-excluded response and do not enter natural feedback totals.

To reduce automated abuse, the Worker derives a daily keyed network token from the Cloudflare connection IP. The raw IP is never written to feedback storage or logs; the token is not joined to a feedback record and expires after about 36 hours. This is a best-effort limit of five new natural feedback submissions per network token per UTC day, not an exact identity or account system.

Operational timing logs

To diagnose slow or failed AI reflections, Yarrow writes an application log entry containing only the request route, requested locale, model name, prompt and output character counts, elapsed milliseconds, and success or error status. These entries do not include your question text, reading text, device identifier, IP address, or request body. They remain separate from the coarse aggregate latency buckets and cannot be joined to a usage event or person.

Daily quota state

To prevent abuse, the Worker keeps a counter per device-UUID, per day (e.g. uuid:20260525 → 7). The counter increments only after a complete AI reflection; timeouts, provider errors, incomplete responses, and client cancellations do not consume the quota. This counter auto-expires after about 36 hours. It is not joined with any other data.

Data retention

On-device data persists until you delete the app or clear the browser data for the web demo. Server-side AI quota counters and feedback abuse tokens expire after about 36 hours. Aggregate usage counters and enumerated feedback indexes expire after about 35 days. Confirmed natural feedback records expire after at most 180 days. We do not log request bodies.

Children

Yarrow is rated 4+ but is not specifically designed for children under 13 and does not request consent under COPPA. Parents are responsible for their child's use.

Contact

Questions or requests: privacy@yarrow.pages.dev.

← back