🐾 Handlr ?

Guide β€Ί Project

Identity β€” the pup, not the box

Your Pup ID β€” a key that is you rather than your server. It signs papers for your badges, carries you to a new box in pup.json, and is what a Handler will collar. How it is kept, sealed, moved and retired, and what its signatures do and do not say.

Tip

New to the Pup ID or packs? Start with Pup ID and packs, explained β€” what they are, and why you might want them.

What ships now

The Pup ID is built: the key, sealing it under a passphrase, seal-at-rest, the recovery sheet, papers for every badge, pup.json, restoring a whole bundle, and moving between boxes β€” and, with Packs, collars and countersignatures. Handles are not built yet.

The idea

A Pup ID is a key that is you. The instance is a place a pup plays; the key is who is playing. It signs the papers for your badges, it is what a Handler's collar will bind, and it moves with you when the box does not.

Make it on the Pup ID page (linked from the header chip and from Settings β†’ System). Until you do, nothing changes: the game works exactly as it always has.

The rules that do not move

  1. The key is the pup, and nobody else ever holds it. It is never escrowed and never regenerated. Losing your box must not lose your pup; losing your key is losing your pup β€” and the app says so at the moment the key is made, not at the moment it is gone.
  2. Badges are stored; papers are signed claims about them. A badge is a row, frozen at the moment it was won β€” its evidence, its module, its XP β€” and never revoked. A paper is your signature on "I earned this, then". Arriving somewhere new never mints: papers you bring are shown as papers, and the new box's badges start empty.
  3. A paper is only as strong as who signed it, and it says who. Your own signature is your own word, shown as your own word. The app never implies more than the signature carries β€” the word verified does not appear anywhere papers are shown.
  4. Identity never depends on a service. No account anywhere is needed to be a pup.
  5. The journal is not the identity. Moving your pup takes the key, the profile, the papers, and the kit and photos you mark to travel. Prose travels only inside the full backup.
  6. Your AI never witnesses you. A local AI Handler runs on your box, under your key. Anything it signed would be you signing twice, so it signs nothing β€” your papers stay self-signed while only an AI handles you.

Making it

The page asks for a name and what you are making the key as β€” a pup, a Handler (a person), or an AI Handler (for an AI kennel's own box). That is your own declaration: it is signed into everything the key signs, nobody checks it, and it can never change. An AI identity can never become a pup.

This key IS your pup

If you lose this box and your passphrase and your recovery sheet, your pup is gone. Nobody can recover it β€” not the app, not the person who wrote it, not a Handler. Your volume backups contain this key in plain form unless you turn on seal-at-rest.

Its fingerprint (four groups of four, AZMI-BDUF-ZSBR-PHGU) is what you read aloud to a Handler to check you are talking about the same pup.

Two keys, and only one is you

Box key Member key (your Pup ID)
Answers which box is this which pup is this
File identity/instance.key, made on first pack use identity/pup.key
Leaves the box never only sealed: pup.key.enc, inside pup.json and the bundle
Signs the envelopes a pack's messages travel in papers, pup.json, the box binding, collars
If lost the next pack sync binds a new one the pup is gone

A box binding is your member key's signed statement that you are on this box, with an epoch that only ever climbs (max(now in ms, the last one + 1, whatever a den has seen + 1)), so a box restored from an old bundle β€” or the same bundle restored twice β€” always binds above what came before. A move is a rebind, not a new pup.

Key custody

  • The file. identity/pup.key (mode 0600, in a 0700 directory) holds the seed. Your volume backups and snapshots contain this key in plain form unless seal-at-rest is on. The Pup ID page warns if either file is readable by anyone else.
  • The sealed copy. Setting a passphrase (at least 12 characters) writes identity/pup.key.enc: the seed under scrypt (n = 2¹⁷, r = 8, p = 1) and AES-256-GCM, bound to your did, your kind and those parameters. That sealed copy β€” never the plain key β€” is what pup.json and the backup zip carry. Changing the passphrase needs the current one β€” and it re-seals the copy on this box only: a pup.json or backup zip you already made still opens with the old passphrase, and a recovery sheet needs none. If one of those leaked, your key leaked; there is no key rotation yet.
  • Seal-at-rest. Keeps only the sealed file on disk: the plain file is zero-overwritten and deleted, and the key is held in memory until the app restarts β€” then it waits, locked, for your passphrase. It needs a seal first (the passphrase form asks twice; this one asks once, and deleting the plain file under a mistyped word would lose the pup), and the passphrase you give here must open it. On a hosted instance your key lives on the host's computer: seal-at-rest keeps it off the host's disk and backups, but the host runs the app that holds it and receives your passphrase β€” a hosted Pup ID is only as private as the host.
  • The recovery sheet. Your key written out by hand (LATCH2-…, groups of four, with check digits that catch a mistyped group). It needs a seal and its passphrase to print, is shown once and never stored, and anyone holding it holds your pup. It is used only to arrive β€” on a fresh box, or back onto a box that lost its key.
  • Wrong passphrases. Every try costs the box a fifth of a second of hard work, so one box takes one try at a time (a second, while the first is being checked, is refused at once) and, after five wrong ones in a row, makes you wait β€” a second, then two, doubling up to a minute. A right one clears the count; restarting the app forgets it.
  • No escrow. There is no "sign in to recover", not ever. This is the one place the app must not be helpful.

Key states

State Means What to do
none no Pup ID on this box make one, or arrive
ready the key is here and matches nothing
locked only the sealed copy is here (seal-at-rest after a restart, or a restore without the passphrase) unlock on the Pup ID page
missing this box knows your did and has no key for it bring the key home: arrive your pup.json or recovery sheet right here (Arriving)
mismatch the key file (or its kind) is not the did this box expects put the right file back, or restore your bundle onto a fresh box (a restore never goes onto a box holding a key file)
moved this box was retired β€” another box holds this pup (the banner says since when, and which box when it knows) bring it back, if you mean to

Every signature fails closed unless the state is ready β€” and while you are packing to move (below). Anything that cannot be signed is simply not sent; nothing is signed with a key that is not yours. A state other than ready puts an amber banner on every page.

Papers

Every badge you hold gets a paper on the next tick: your key's signature on the badge's title, icon, period, frozen evidence, XP, the module that awarded it, and the instant it describes. A kit badge names the item by its travelling uid, never by its name. The trophy case is Pup ID β†’ Papers.

  • A paper's id is the sha256 of what was signed, so re-signing an identical claim is the same paper, and a countersignature added later never changes which paper it is.
  • Its serial counts up per box. A pup running on two boxes at once could reuse a serial; the ids stay unique.
  • Undo does not touch a paper. It is a record of what was true when it was signed β€” the same promise a stamped photo makes. Badges are never revoked, so neither are papers.
  • Papers brought from another box are your history: shown under Brought with you, counted toward nothing here β€” not XP, not rank, not your badges, not a share card.
  • Papers signed by an earlier Pup ID β€” a backup restored without its key, then a new Pup ID made or arrived on that box β€” stay, under Signed by an earlier Pup ID. They are never shown as your word and never travel in pup.json; every badge you hold gets a fresh paper from the key the box holds now. (A name a Handler gave the old key with their collar does not carry over to the new one either.)
  • A summary paper is a dated table of figures, as signed; not recomputed. pup.json carries a lifetime one.

What a countersignature says

With packs, a Handler can countersign a paper. Read it for exactly what it says:

  • on a collar paper, attests: party β€” "I am the other party to this collar";
  • on a badge or summary, attests: in window β€” "this is dated inside my collar", not "I saw it happen". A summary lists which of its figures the Handler checked against their own records.

A signer's kind is their own signed declaration β€” "Sir signed that they are a person", "Byte signed that it is an AI Handler". It is attributable (a lie is signed) and never verifiable. A did that has signed both is shown as disputed, and nothing is ever silently marked bad: a signature that does not match reads "read this as unsigned".

Stray and collared

Stray β€” no Handler. Every paper is your own word. This is not a lesser state; most pups are strays most of the time. Collared (with packs) β€” a Handler's collar, and papers from inside it may carry their countersignature. The header chip says which: 🐾 stray, πŸ”— Sir, 🐾 stray Β· πŸ€– your AI.

A collar that ended is not a collar that never happened: its papers stay. (The level-one rank is runt, so stray means only the social state.)

Moving to a new box

Moving is explicit:

  1. Prepare to move (Pup ID page). Everything that talks to anyone else β€” packs, your AI, papers, bindings β€” stops, so nothing is signed twice from two boxes. Your own game keeps working, the emergency key included. Cancel any time.
  2. Take your pup: the whole game (bundle.zip, restored with python -m handlr.restore), or just the pup (pup.json, arrived on the new box). pup.json carries no pack: each den keeps your membership through this box and refuses a join from the new one until it lets go of this one, so leave your packs before you prepare β€” the Pup ID page and the export page name every pack a pup.json would leave behind, with a link to leave each. The bundle keeps your packs.
  3. Once your pup is alive on the new box, Retire this box: its plain key is zero-overwritten and deleted; the sealed copy stays. It refuses while no sealed copy exists β€” this box would be holding the only one β€” and it asks for your passphrase, which must open that sealed copy: the proof that the copy the new box relies on works, before this box lets go of the plain key. A retired box records no fingerprint of where you went (it cannot know). It is no longer "packing": it has moved β€” Cancel is gone, and bringing it back is the only way home. While you are packing, and once this box is retired, a Handler's, a pack's or your AI's command is refused as paused β€” and one sent meanwhile never lands afterwards: not after Cancel, not after bringing the pup back, and not on a box restored from a bundle made while packing (the record of when you were away rides the restore).
  4. Bring my pup back to this box (passphrase and a tick) undoes a retire and bumps the binding epoch, so every den re-binds to it.

With packs, a den that has seen your key on a newer box answers the old one with that box's binding, signed by your own key β€” and the old box retires itself. If there is no sealed copy to keep, it does not shred the key; it stops signing and says, in red, that your key was copied. An unsigned "you are gone" from a den never changes anything.

Two exports, two jobs

bundle.zip β€” the whole game pup.json β€” your pup
For moving everything, restoring a machine arriving on your own new box with your identity
Key pup.key.enc when you set a passphrase the sealed key β€” required
Log, reflections, prose every table, verbatim never
Badges rows and papers papers only; the new box's badges start empty
Kit everything toys and gear marked 🧳, owned or retired β€” no notes, no prices, no text fields
Photos the originals photos marked 🧳, re-encoded (no location, no camera details, no caption)
Grants, packs yes β€” and paused on restore no

pup.json holds your sealed key β€” never hand it to anyone

Not a Handler, not a pack, not a support thread. Whoever has it and your passphrase is your pup. It is for arriving on your own box. A papers-only file to show other people is future work.

Arriving

On a box with no Pup ID, the Pup ID page takes your pup.json and its passphrase β€” or your recovery sheet alone and a new passphrase, typed twice, to seal it there. A box whose key went missing takes its own key back the same way, and nobody else's: only the key comes home, with any papers the box lacks. The game on that box stays exactly as it is β€” its kit, its photos and its profile are its own, so a toy you deleted there after making that pup.json stays deleted. It checks everything before it writes anything: the file's signature under its own did before the key is tried, the key against the passphrase (and its kind against the file's), every paper against your key, every photo's hash and size before the image decoder sees its pixels. A countersignature that does not check out is dropped and said so; the paper stays yours. A name "given by" a Handler arrives only with a collar paper that Handler countersigned over that very collar β€” and the name shown as the giver is the one the Handler's collar paper carries, never one the file adds. The photos are decoded and written before the database is touched (a box full of photos never holds up your emergency key while it works); the key files are written last, and together with everything else or not at all.

It never mints: one Arrived from another box line in the log, no XP, no badge, no counted photo. Carried kit does not count toward the kit-collection badges. Carried photos show on their item and under What travels, and may travel again.

Not built yet

  • Handles (a Matrix, fediverse or Bluesky name attached to the key, proven once, revocable) β€” planned. The key is never derived from a handle.
  • Pup key rotation after a compromise, and a papers-only file for others.

Questions, answered

  • A paper when the event under it is undone later? The paper stands, dated β€” see above.
  • More than one identity? One key, one pup, one box: a second persona is a second pup with its own box, and the app is not the thing that links them.
  • Is a lost key recoverable? No, loudly, at generation β€” with the optional recovery sheet.
  • Do papers expire? Never. They are dated; a reader can weigh an old one.
  • Whose papers does the trophy case show? Yours. A pack-mate sees only what you grant, paper by paper.
  • Does a pup name themselves, or does a Handler? Both, and the record keeps who: a name you were given (with a collar) is kept apart from one you picked, and choosing your own clears it.

The consent rules in The spirit of the game and in Packs outrank everything here.

This page is docs/identity.md in the repo. Spotted a mistake? Tell us how to report it β†’