A phone you come home to

andashi is a GrapheneOS phone declared in code. What the zones are, which app lives in which of them, what each one may reach, down to the home screen — all of it is files. You change a file, and the phone answers with what it actually applied, not with the hope that it worked.

Early, and breaking changes are the normal case. It runs against a phone that already has GrapheneOS on it — installing that is what wipes a device, not this.

The andashi mark: a sliced sun over a glazed bowl

The name

an + dashi

Dashi is the stock a Japanese kitchen starts from: kombu, katsuobushi, water. You never taste it on its own, it is in everything, and it is the reason the rest works.

That is what a distribution is. The base everything else is cooked in, and when it is any good, nobody notices it — you notice the phone, not the six files underneath. The an is the Android part, and the mark is the sun those files put on every home screen.

Configured, not clicked

The whole phone, declared

The zones are not set up by hand and then described — they are described and then set up. Six files hold the phone: what a zone is, which apps live in it, what they may reach, how it looks. Every deterministic setting is a key in one of them.

config/profiles.json
What a zone is: its VPN and whether that VPN locks the zone down, whether it runs, is on demand or stays stopped, how it unlocks, whether Play services exist in it at all.
config/apps.json
Which app lives in which zones, where its binary comes from, whether it may have the INTERNET permission, and which scopes it gets instead of full access.
config/settings.json
Android settings per namespace — globally, for all profiles, or for one zone.
config/theming.json
One source for the visual identity: Monet palette per zone, wallpaper, status bar, keyboard, and which launcher holds the HOME role.
config/features.json
The platform features a zone uses: work profile, private space, inter-profile sharing.
config/launcher/*.json
Generated from the above, one file per zone, pushed into the launcher and read back.
{
  "key": "anon",
  "label": "Anon",
  "zone": "—",
  "create": true,
  "runtime": "stopped",
  "vpn": "orbot",
  "vpn_pkg": "org.torproject.android",
  "vpn_lockdown": true,
  "vpn_note": "Mandatory: without it, traffic leaves past Tor.",
  "play": "none",
  "unlock": "password only, no fingerprint",
}

A zone in profiles.json. The lockdown flag is not a preference — for Anon it is the difference between Tor and a leak.

{
  "schemaVersion": 1,
  "icons": { "themed": true, "pack": "app.lawnchair.lawnicons" },
  "appearance": {
    "transparency": { "name": "fold-glass", "background": 0.31 },
    "wallpaper": { "image": "home.jpg", "target": "both" },
  },
  "home": {
    "searchBar": { "position": "bottom" },
    "dock": { "favorites": [{ "packageName": "org.thoughtcrime.securesms" }] },
    "widgets": { "enabled": true, "widgets": ["weather", "calendar"] },
    "clock": { "style": "digital1", "fillHeight": true },
  },
}

Its home screen, generated from the same sources and pushed per Android user. Comments and trailing commas are fine.

# push the config into the launcher of Android user N
adb shell content write --user N \
  --uri content://org.andashi.home.config-ingest/launcher.json < launcher.json

# ask it to reload, then read the effective state back out
adb shell content call --user N \
  --uri content://org.andashi.home.config-ingest --method reload

How it reaches the phone. Per Android user, without root.

The Home zone's home screen: clock, wallpaper, search bar
What that file produces: the Home zone, straight out of a provisioning run.

Re-running is the normal case, not the emergency: every step checks before it writes, and pushing an unchanged file changes nothing. A zone can be changed long after it was created — its apps, permissions, settings, VPN, theme and home screen are all re-applied in place.

The model

Six zones, one phone

The phone is treated as a segmented network. The question when installing an app is never “do I want this app?” but “which zone is this app allowed to do damage in?”. Android allows exactly one VPN per profile, so the assignment is enforced by the platform rather than by discipline.

Wallpaper of the Home zone

Home

Network analogue
trusted LAN
Runtime
always
VPN
Tailscale, private
Lockdown
no
Play services
none
Unlock
fingerprint + PIN

No Play services. Hard rule, not negotiable. Work lives inside Home as a managed profile.

Wallpaper of the Cloud zone

Cloud

Network analogue
DMZ
Runtime
always
VPN
RethinkDNS, monitoring
Lockdown
no
Play services
sandboxed, throwaway account
Unlock
PIN

Where an app may talk to whoever it likes, and nothing else can see it.

Wallpaper of the Gadgets zone

Gadgets

Network analogue
IoT VLAN
Runtime
on demand
VPN
RethinkDNS, aggressive
Lockdown
yes — no connection without the VPN
Play services
sandboxed, throwaway account
Unlock
PIN

Lockdown is what keeps the IoT zone from bypassing its own filter.

Wallpaper of the Ops zone

Ops

Network analogue
management VLAN
Runtime
stopped
VPN
Tailscale, business
Lockdown
no
Play services
none
Unlock
fingerprint + PIN

Stopped means the keys are evicted: encrypted at rest, not merely in the background.

Wallpaper of the Lab zone

Lab

Network analogue
guest VLAN
Runtime
on demand
VPN
as needed
Lockdown
no
Play services
as needed
Unlock
PIN

Wiped often. Real amnesia here is pm clear and a re-run, both scripted.

Wallpaper of the Anon zone

Anon

Network analogue
Runtime
stopped
VPN
Orbot
Lockdown
yes — no connection without the VPN
Play services
none
Unlock
password only, no fingerprint

Lockdown is mandatory: without it, traffic leaves past Tor silently.

Work is not a zone of its own: it is a managed profile inside Home, with its own VPN and lockdown, and its apps show badged in Home's launcher.

Nothing to arrange

Six home screens, one page each

A clock, the search bar, and whatever the dock and the widgets hold. Apps live in search, not on the desktop, so the home screen never fills up and never needs tidying. The wallpaper is how you know which zone you are in.

The Home zone's home screen
HomeThe colourless one. Colour signals that you are not home.
The Cloud zone's home screen
CloudThe DMZ, where an app may talk to whoever it likes.
The Gadgets zone's home screen
GadgetsThe IoT zone, behind its own aggressive filter.
The Ops zone's home screen
OpsManagement. Stopped until it is needed.
The Lab zone's home screen
LabGuest network. Wiped often.
The Anon zone's home screen
AnonOrbot, lockdown, no fingerprint.

The same launcher, a different catalogue

Which app exists in a zone is declared in apps.json, so the drawer differs per zone. Home has a Work tab — the managed profile inside it — and carries the identity and secrets apps. Anon has neither, and no Play services anywhere near it.

The Home zone's app drawer
HomePersonal and Work, side by side.
The Anon zone's app drawer
AnonOne profile, and only what it needs.
The hard part

Configuration without a backdoor

Configuring Android as code usually means root, a privileged system app, or an accessibility service that can read the screen. All three open a hole in exactly the place GrapheneOS closes. So none of them is used.

adb, not root

Three shell commands per Android user. No root, no Magisk, no system app, no accessibility service, nothing left running on the device that could be talked into anything later.

A sandbox it cannot leave

The launcher takes its configuration through a content provider into its own files directory. It holds no system powers and needs no permissions to be configured — the config can only reach the launcher, never the platform.

Read back, not reported

Provisioning does not trust an exit code. It writes, triggers a reload, reads the effective state and the diagnostics of that reload back off the device, and fails when they disagree with the file.

Honestly

What it does not promise

A hardening project that only lists advantages is not credible. These are the limits, and they are not going to be buried in a FAQ.

What you need

Nine minutes and a phone with GrapheneOS on it

A device

A supported Pixel with GrapheneOS already installed and set up. That install is the step that unlocks the bootloader and clears the device.

A computer

Anything with adb and jq. No agent runs on the phone.

About nine minutes

For all six zones. Running it again is the normal case, not the emergency.

No newsletter, no Discord. Watching the repositories is the way to follow this.

Reasonable objections

Why not just a shell script?

Why not LineageOS and a few shell commands?
Because a script tells you it succeeded. The interesting part here is not writing settings, it is reading them back and failing loudly when the device disagrees with the file — and doing that per profile, six times, without root. The GrapheneOS base is the other half: verified boot and a hardened runtime are not something a script adds later.
Why not Magisk modules, or a configuration app?
Both need the permissions that make the hardening pointless. Root and an accessibility service are exactly the holes this is built to avoid, so configuration goes through adb from outside and nothing privileged stays behind on the phone.
Can I run it on a phone that is not a Pixel?
No. GrapheneOS supports Pixels, and everything here assumes it. The zone model would port to plain Android, but without verified boot and the hardened runtime it would be a different, weaker thing.
Six profiles sounds like a lot of switching.
It is. Two of them are stopped most of the time and two run on demand, because Android only runs three at once. If you want one phone for everything with no seams, this is the wrong project.