America turns 250 today — happy Independence Day to everyone celebrating far from home.

Privacy Architecture

Why Boris has no backend (and what that costs us)

The honest engineering story of how Boris™ protects your data — by not having it.

Last updated: 2026-07-24

The deal

The promise

Your phone is the only place Boris stores anything about you.

Boris doesn’t have your data because Boris never asked for it. There’s no account to create, no email to enter, no password to remember. The app works the moment you open it, and everything you do — every task you check off, every business you save, every preference you set — stays on your device. Nothing is uploaded. Nothing is synced. Nothing is shared.

If you delete the app, your data goes with it. If your phone breaks, we can’t restore it. If Boris is sold or shut down tomorrow, no one inherits anything about you, because there’s nothing to inherit.

That’s the deal. The rest of this page is the honest version of how it works, what it costs you, and what it costs us to keep it that way.

What we don't do

What this means in practice

Things Boris does not do, by design:

  • No account, no login, no email, no password
  • No analytics on people — we don’t count installs, we don’t measure feature usage, we don’t track sessions, we don’t build a profile of anyone who opens the app
  • No advertising IDs, no fingerprinting, no third-party trackers
  • No location tracking
  • No push notifications driven by anything we know about you (the notifications you get are scheduled locally on your phone for tasks you set up — they never leave your device)
  • No data sales, no “anonymized data shared with partners,” no data-broker pipeline
  • No marketing emails, because we don’t have your email
  • No re-engagement campaigns, because we don’t know who’s stopped using the app
  • No A/B tests on your behaviour, because we can’t see your behaviour

We didn’t disable these things or turn them off. We never built them.

Boris never tracks you. No accounts, no ads, no analytics on people. On paid partner listings, Boris counts — anonymously and in aggregate — how many times the listing was shown and tapped. Never who. Nothing Boris stores can identify you, follow you, or single you out. Free listings and every page of editorial content still emit nothing, per-listing, ever. The exact mechanics are named in the network calls section below, with the app version each one starts in.

Every network call, named

Every network call Boris makes, named

Boris is not a fully offline app. It reads content from one host, checks a currency rate from one external API, sends a correction only if you choose to, and — starting with app version 1.4.4 — counts anonymous taps on paid partner listings, with anonymous impression and category-browse counts following in app version 1.4.5. Below is every one of those calls, including all fifteen content endpoints by name, so a technical reader can check this list against a live network trace and find nothing missing.

Read-only fetches (anonymous)

These sixteen fetches — fifteen content endpoints plus the currency check — request public information and identify nothing about you. Each is an anonymous HTTP GET. None carries a user ID, session ID, account, or fingerprint.

  1. Currency rates. The exchange-rate widget on the home screen fetches the current USD/EUR rate from the Frankfurter API (api.frankfurter.dev), which wraps European Central Bank reference rates. Anonymous, cached for the day.
  2. alerts. Active KMC-wide notices — office closures, region-wide warnings — fetched on app open.
  3. boris-says. The contextual tips and callouts Boris shows throughout the app.
  4. checklist-content. Task descriptions, hours, addresses, and tip text inside PCS checklist tasks.
  5. checklists. The checklist structure itself — which tasks exist and how they’re grouped.
  6. directory. The KMC business directory listings.
  7. entitlements. Reference information on entitlements by status (active duty, GS, contractor, NAF).
  8. events. Recurring KMC and German community events.
  9. guides. Longer-form guide content.
  10. kuendigung-templates. Template text for German cancellation letters (Kündigung).
  11. money-tips. Money-saving tips content.
  12. offices. Hours, addresses, and contact information for KMC offices.
  13. partners. Directory partner (paid business) records.
  14. places. Map points of interest.
  15. recommendations. The “while you’re free” recommendation content.
  16. splash. Configuration for the welcome screen — seasonal updates, community-wide notices.

All fifteen content endpoints live on one host — borispcs.com/boris-cms/api/app/ — reachable only over HTTPS. That host and the Frankfurter API receive the standard HTTP metadata any web request includes — IP address, user-agent, request time — but no Boris-specific identifier that would let them link your fetch today to your fetch yesterday or to anyone else’s. Nothing in the response is tied to who’s asking.

Some of that content points at images — a partner’s logo, a splash-screen graphic. Those aren’t a separate system: the app fetches the image URLs contained in the content above and caches them on your device, the same anonymous way, so it isn’t re-downloading the same picture every time you open a screen.

Voluntary write (you trigger it)

  1. Corrections. If you tap “Report a problem” on a checklist task — for example because a phone number is wrong or hours have changed — the app sends four things: the task identifier, the task title, the correction text you typed, and an optional source citation you typed (e.g. a URL or “told at VAT office”). That’s it. No name, no email, no device ID, no location, no account. The submission goes to boris-corrections.criko3d.workers.dev, which forwards it into a private GitHub issue tracker that only the Boris team can see, and it gets used to fix the content for everyone. You see exactly what gets sent before you send it. Both text fields are filled in by you. You opt in to each correction.

Anonymous counting (paid partner listings only)

  1. Partner tap counting, starting with app version 1.4.4. On paid (“Gold”) partner listings only, tapping Call, WhatsApp, Website, Email, or Directions fires a fire-and-forget POST to borispcs.com/partner-api/beacon/, with the action and the partner’s listing ID as the next two path segments — for example borispcs.com/partner-api/beacon/call/used-car-guys-kaiserslautern. No request body, no user ID, no device ID, no session ID, no cookie. At the Cloudflare edge, a two-letter country code is derived from the request IP and a platform (iOS / Android / other) is derived from the User-Agent; the IP address and User-Agent are discarded immediately after — never stored. What’s stored is a running count per combination of partner, action, day, country, and platform. There is no field in that count for a person. Tapping the same button on a free or non-partner listing fires nothing at all.
  2. Partner impression counting and category demand, starting with app version 1.4.5. Two more anonymous counts ship in the following release, days after 1.4.4. (a) Impressions: when a paid partner’s listing is shown on the directory, the Boris Suggests card, or the recommendations screen, the app batches a bare count on-device and sends it at most once a day per partner and surface — no per-view timestamp, no event order, so a session can’t be reconstructed, not even by us. (b) Category demand: an anonymous count of how often each directory category is opened, stored per combination of category, base, and calendar week. Categories, never people, and never individual free listings. Both use the same fire-and-forget, no-identifier posture as the tap beacon above, on the same host.

That’s the complete list: fifteen content reads, one currency check, the images that content points to, one voluntary correction if you choose to send it, and — once the respective app updates are live — anonymous tap counting on paid partner listings (1.4.4) plus anonymous impression and category-demand counting (1.4.5). Nothing else talks to the internet, and nothing tied to a free listing or to editorial content ever does.

The honest tradeoff

What this costs us

The privacy posture above is not free. It’s a real engineering and business constraint, and it costs us things you should know about:

  • We can’t tell you how many families use Boris. When we say “the app is being used in the KMC,” we mean it the way you’d mean it about a restaurant — we hear about it from people, we don’t have a counter.
  • We can’t fix bugs based on what users do. If a task is confusing and people skip it, we have no way to learn that from data. We learn from people telling us, the way you’d tell a friend.
  • We can’t improve features based on usage signals. Most apps optimize what they show you using behavioural data. We optimize using judgment, conversations, and careful documentation about what KMC families actually need.
  • We can’t market to lapsed users. If someone stops using Boris, we can’t reach them, because we don’t know who they are.
  • We may grow more slowly than a product that captures everything and optimizes for engagement. We’re betting that’s worth it.
  • We’re slower at detecting stale content because we don’t see signals like “this address is the most-clicked one and last week 30 people reported it broken.” We rely on a separate verification cadence that re-checks content on a schedule whether anyone reports it or not.

We chose to pay these costs. We think the trade is the right one for a product KMC families use during one of the most exposed periods of their lives. But we owe you the honest version of what we gave up to make it true.

What you get back

What this gives you

  • Your PCS journey is yours alone. No one knows you’re moving except the people you tell. Boris doesn’t know.
  • No one knows what tasks you’ve checked off, what businesses you’ve saved, or what mode of the app you’re in. That state lives only on your device.
  • Your data is not on a server that could be hacked, because there is no server with your data on it. There’s nothing for an attacker to steal at our end. Even Boris’s own infrastructure has none of your information on it.
  • If Boris is acquired, sold, or shut down, no successor inherits anything about you. There’s nothing to inherit.
  • If a government agency in any country issues a subpoena for “all Boris user data,” the honest answer is that there is none to hand over. We engineered the absence on purpose.
  • You can use Boris under SOFA status, as a contractor, as a NAF civilian, as a German national, or as a curious civilian without any of those statuses leaving your device. Boris doesn’t ask, doesn’t collect, and doesn’t tell.

Don't take our word

How you can verify this is true

We don’t expect you to take our word for it. Here’s how to check:

  • Watch the network. Any technical reader can put Boris through a network proxy (Charles, mitmproxy, Wireshark) and observe every byte the app sends. You should see requests to exactly three hosts: borispcs.com (the fifteen content endpoints, the images they reference, and — once app version 1.4.4 is live — the anonymous partner-counting beacon under /partner-api/), api.frankfurter.dev (currency), and, only if you submit a correction, boris-corrections.criko3d.workers.dev. Nothing else. As of 2026-07-24, app version 1.4.4 has been submitted to Apple for review and is not yet live — a proxy trace of the current App Store build will show none of the beacon calls described above. Once 1.4.4 is live, you’ll see a POST to borispcs.com/partner-api/beacon/ only when you tap Call, WhatsApp, Website, Email, or Directions on a paid (“Gold”) partner listing — never on a free listing, and never anywhere else in the app. Version 1.4.5 adds impression and category-demand counts on the same host under the same posture, batched and sent at most once a day.
  • Read the legal documents. The Datenschutzerklärung and Impressum are public, dated, and lay out the same posture in legally precise language. They’re aligned with this document, not in tension with it.
  • Compare claims to behaviour. If we ever describe a feature here that involves data leaving your phone, this document tells you exactly what data and exactly when. If you ever observe a network call to a host other than the three listed above, that’s a bug — please report it through the corrections form in the app.

The why

The principle underneath this

Trust is not a marketing word at Boris. It’s an architectural choice we made before writing the first line of code, and one we keep paying to maintain.

The principle is simple: most apps treat your data as the asset and your trust as the cost. Boris does the opposite. Your trust is what makes Boris worth using. The absence of your data on our infrastructure is the proof that the trust is earned, not asked for.

This is the same principle that made us:

  • Refuse to add Google Analytics, Facebook Pixel, or any tracker
  • Refuse to integrate ad networks, even ones that “respect privacy”
  • Refuse to require an account, even though it would have made some features (sync across devices, recovery after phone loss) much easier to ship
  • Refuse to sell, share, or “monetize” the directory of KMC businesses by selling access to “user intent data”
  • Choose the legal posture of operating fully under German DSGVO and TMG rules, not the lighter footprint of a US-based startup

Each of those decisions was a real cost. We wrote them down so future versions of Boris stay honest.

The technical choice

What we built instead of accounts

The technical choice underneath all of this: every piece of state Boris keeps about you lives in AsyncStorage — the standard local-storage mechanism React Native apps use on iOS and Android. Your tenure date, your selected status (active duty / GS / contractor / NAF civilian / German civilian), your saved places, your completed tasks, your dismissed banners — all of it lives in the Boris app’s private sandbox on your phone, isolated from other apps by the operating system.

The cost: lose your phone, lose your Boris state. We have nothing to restore from. There is no “log in on a new device” because there is no account.

The benefit: nothing about you exists anywhere except on the device you hold in your hand.

We considered building a sync feature with end-to-end encryption. We chose not to. Even with end-to-end encryption, the metadata of “Boris user X has Y devices that sync at time Z” is information we don’t want to have. The simpler choice — no sync, no recovery, no infrastructure — is the more honest one.

If you want belt-and-suspenders before switching phones, take a screenshot of your checklist progress. Lo-fi, but it works, and it costs you nothing in privacy.

Specifically for the KMC

Why this matters for KMC families

You are already living a life with more data exposure than most. Your information sits in DEERS, MyPay, AAFES, OPM, the Defense Health Agency, USAFE / EUCOM / U.S. Army Garrison systems, the German Bürgeramt and Krankenkasse, your housing portal, your car insurance database, your TLA receipt records, your school enrolment systems, and more. Each of those is necessary. Each requires trust.

The last thing you need is another app quietly adding to that pile.

Boris is the one place in your KMC stack that simply doesn’t need anything from you. You can use it during your most chaotic week, your most stressful task, your most uncertain decision — and it will help without taking. That is the contract.

Our public commitment

Holding ourselves accountable

This document is dated 2026-07-24.

Correction, 2026-07-23: until today, this page said Boris made six network calls. That was wrong. As we shipped features between roughly April and July 2026, each one added a content endpoint, and we kept saying six instead of updating the count. The real number is fifteen content-read endpoints, plus the currency check, the voluntary correction submission, and the images that content points to — all listed above by name. No new host is involved, no user data changed hands, and the character of every call is the same as it always was: anonymous, read-only, content-only. We caught it, and we’d rather post a visible correction than quietly edit the number and hope no one noticed.

Amendment, 2026-07-24 (fence v3): starting with app version 1.4.4, Boris counts anonymous taps on paid partner listings — Call, WhatsApp, Website, Email, Directions — as a running total, never as a per-person record. App version 1.4.5, days later, adds anonymous impression counts on paid partner placements and an anonymous count of how often each directory category gets opened. This is a deliberate widening of what Boris counts, decided and named on purpose, not something shipped quietly and left for someone to find in a network trace. Free listings and all editorial content still emit nothing, per-listing, ever. The claim on this page moves from “Boris collects nothing” to “Boris collects nothing about you” — we’re not going back to the old wording, because it would no longer be true, and we’re not staying quiet about the new one either.

If our architecture changes — if we ever add a network call, change what data is collected, modify the corrections payload, or add any kind of account or sync — we will update this document, date the change, and say what changed and why. Old versions will remain accessible.

If a future version of Boris ever takes a step away from the posture described here, that step will be visible, dated, and explained — not hidden in a privacy-policy update at the bottom of an email. We hold ourselves to that standard, and we want the standard public so you can hold us to it.

For ACS / FSS / LRMC / garrison

A note for institutional readers

If you’re evaluating Boris on behalf of an organization that supports KMC families (ACS, FSS, LRMC, garrison welcome programs, AFTB, command sponsorship offices), the privacy posture above is intentional and durable. It’s not a phase, not a limitation we plan to “grow out of,” and not contingent on funding.

Boris was built specifically so that any institution recommending it to families could do so without a third-party data review, because there is no third-party data flow to review. We can’t share data we don’t have.

We are happy to walk through the architecture in detail, demonstrate the network behaviour live, and answer specific questions from institutional evaluators. Reach us through the corrections form in the app or through the Impressum contact at borispcs.com/impressum.

For engineers + auditors

A note for technical readers

The architecture summarised here:

  • State management: React Native + AsyncStorage. No remote persistence layer, no sync, no cloud backup integration with the user’s iCloud or Google account.
  • Network layer: Fifteen content-read endpoints under borispcs.com/boris-cms/api/app/alerts, boris-says, checklist-content, checklists, directory, entitlements, events, guides, kuendigung-templates, money-tips, offices, partners, places, recommendations, splash — plus one currency-rate read (api.frankfurter.dev) and one voluntary write (corrections). All content and currency requests are anonymous GETs; the images referenced inside that content are fetched and cached the same way. No telemetry SDK, no crash reporter that uploads, no third-party analytics.
  • Counting layer (paid partner listings only): starting with app version 1.4.4, tapping a paid listing’s Call / WhatsApp / Website / Email / Directions action fires a fire-and-forget, bodyless POST to borispcs.com/partner-api/beacon/ (action and partner ID as path segments). Country (2-letter, from request IP) and platform (from User-Agent) are derived at the Cloudflare edge; IP and User-Agent are discarded, not stored. Stored state is one counter per combination of partner, action, day, country, and platform. App version 1.4.5 adds a batched impression counter (per partner and surface, flushed at most once a day, no per-view timestamps or ordering) and a category-demand counter keyed to category, base, and calendar week. No schema field exists for a user or device identifier in any of these tables.
  • Build pipeline: No injection of tracking libraries during build. Production builds contain only the code in the source.
  • Notifications: All scheduled locally via the OS notification scheduler (Apple’s UNUserNotificationCenter / Android’s local notifications). No push tokens are registered with Boris servers because there are no Boris servers to register them with.
  • Font loading: Fonts (Inter, Plus Jakarta Sans, JetBrains Mono) are loaded from @expo-google-fonts/* npm packages that bundle the font files into the app binary. No runtime fetch from fonts.googleapis.com or fonts.gstatic.com. The same fonts are self-hosted on Cloudflare Pages for the website (no CDN dependency for fonts there either).
  • Compliance posture: DSGVO + TMG aligned. The website’s Datenschutzerklärung is the legally precise version of this document and discloses that Cloudflare Pages (the host behind borispcs.com, serving both the website and the app’s content endpoints) processes standard server-log metadata under Art. 6(1)(f) DSGVO.
  • Corrections endpoint: boris-corrections.criko3d.workers.dev, a Cloudflare Worker that proxies the four-field payload (taskId + taskTitle + correctionText + source) into a private GitHub issue. The Worker logs nothing it doesn’t need; the GitHub repo is not public.

If anything in the live app behaves differently than what’s described here, that’s a bug we want to know about. Network proxy traces are the canonical evidence; please send them through the corrections endpoint or via the Impressum contact.

← Back to borispcs.com