← Blog ·

Koove App-Init Explained: What Gets Generated, What Gets Stored, and What Never Leaves Your Device

The first time a mobile app connects to Koove, it kicks off a process we refer to as koove app-init: the app attests itself as a real physical device, registers with Koove's backend, and generates the cryptographic material that controls which secrets it can ever open. Understanding exactly what happens in that step is the fastest way to decide whether you trust a zero-knowledge secrets manager.

What koove app-init actually is

In the mobile SDK (@koove/sdk, React Native/Expo, dev build required), this whole process is one call:

import { KooveClient } from '@koove/sdk';

const client = new KooveClient({
  apiUrl: 'https://api.koove.io',
  appId: 'app_xxx',
  appToken: 'xxx',
});

await client.init(); // attests the device + registers it

init() does two things in one pass: it requests hardware attestation (App Attest on iPhone, Play Integrity on Pixel/Android) and registers the device against your Koove project. There's no developer bypass on either platform — attestation runs against real physical hardware, full stop.

What gets generated: the local "controller keyfile"

During init(), the device locally generates an X25519 identity key pair (generateIdentityKeyPair, from @koove/crypto, re-exported by the SDK). You don't need to call it yourself — client.init() handles it for you.

We informally call this key pair the controller keyfile: it's the material that controls which secrets that specific device is allowed to open. The private half:

  • is generated on-device,
  • lives in the platform's secure storage (Keychain/Keystore),
  • is never transmitted in plaintext to any server.

The public half does travel to Koove's backend, tied to the device's attestation via computeAttestationBinding. That's the only piece Koove needs in order to later wrap secrets that device is authorized to open.

What gets stored on Koove's server

Once init() completes, the server side holds:

  • the device's public key (registered via addRecipient),
  • the attestation binding tying that public key to a verified physical device,
  • the encrypted envelopes (AES-256-GCM, with HKDF-SHA256 key derivation) for every secret that device is authorized to see.

Koove's server never sees a plaintext secret. It only ever stores ciphertext and who's allowed to open it.

What never leaves your machine

  • The X25519 private key generated at init() time — the controller keyfile itself.
  • The plaintext value of any secret: it's decrypted in memory, on-device, behind biometrics, via decryptSecret.
  • The 24-word recovery mnemonic (BIP39, generated with generateRecoveryCode) — unless you choose to write it down somewhere yourself.

Where the secrets a device can open actually come from

The normal flow starts on the CLI, often run by your own AI coding assistant right after it provisions a new credential:

koove set STRIPE_SECRET_KEY sk_live_51... --env prod

Your codebase only ever references the name (STRIPE_SECRET_KEY), never the value. Once a device is approved as a recipient, Koove re-wraps that secret for its public key (sealKey/openKey under the hood), and the device can request and decrypt it locally.

Approving and revoking devices

Managing who holds a valid controller keyfile for your project happens through the CLI:

koove devices
koove device-approve dev_8f3a
koove device-revoke dev_8f3a
koove rewrap

device-revoke drops that device from the list of future recipients, and rewrap regenerates the envelopes without it. Worth being blunt here: revoking a device does not delete secrets that device already decrypted and cached in memory or local storage before revocation. Revocation blocks future access — it doesn't undo past downloads.

What happens if you lose a device

If you lose the phone, that device's controller keyfile is gone with it — it lived in that device's secure storage, not on a server. To regain access from a different, already-authorized device:

koove recover-show

Which reveals the 24-word mnemonic derived with deriveRecoveryIdentity. With that mnemonic you can re-derive a recovery identity and regain access to any secrets you're still authorized for — but let's be precise about what this isn't: Koove's envelope scheme has no forward secrecy and no ratchet, so don't compare it to Signal-grade messaging properties. It's solid envelope encryption, not a messaging protocol.

Limits worth knowing about

  • Attestation requires real hardware and has no developer bypass on iOS or Android — great for production, slower for quick emulator testing.
  • The scheme is X25519 + AES-256-GCM with HKDF-SHA256, without a ratchet: there's no forward secrecy if a past private key is ever compromised.
  • Koove can't retroactively erase a secret that already left the device before you revoked access.

For the full architecture and trust model, the details are documented at /security.

Why this matters when your code is written by an AI

In projects where a large share of the code comes from an AI assistant, hardcoded keys sitting in the repo are the failure mode we see over and over. With koove app-init in place, your assistant can run koove set directly and your code only ever references names — never values. We go deeper on this problem at /ai-security.

Getting started

Full CLI and SDK command reference lives at /docs/sdk, and available plans (including a free tier to get started) are at /plans. If you still have questions about attestation, revocation, or recovery, check the /faq.

Want to see koove app-init running in your own project? Sign up for Koove and run your first client.init() in minutes.

AI-generated code

Your AI writes the code. Who guards the secrets?

The most common security failure in AI-generated apps is exposed credentials. With Koove, your own assistant stores every token from the CLI — encrypted on your machine, never in the code or the repo.