repo.box Spec: Identity
Overview
Identity is the foundation layer. It answers one question: who signed this request?
Core Principles
- An identity is an EVM address. Period.
- The system never stores ENS names, usernames, or display names in authoritative config.
- ENS resolution is a UI convenience, resolved on-the-fly, never persisted.
- "Access follows a name" (e.g.
treasury.dao.eth) is a groups concern, not an identity concern.
Identity Format
Identities are represented as typed strings with a prefix:
evm:<checksummed-address>
Example: evm:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
The evm: prefix exists to future-proof for other identity types (SSH keys, GitHub accounts, etc.), but EVM is the only supported type at launch.
Authentication: ERC-8128 (Hard Requirement)
All HTTP authentication uses ERC-8128 Signed HTTP Requests. No JWTs. No API keys. No session tokens. No SIWE-then-session pattern.
Every request is individually signed per RFC 9421 with an Ethereum account.
Spec References
- ERC-8128: https://erc8128.org
- Library:
@slicekit/erc8128 - RFC 9421: HTTP Message Signatures
- ERC-191: Signed Messages
- ERC-1271: Smart Contract Account verification
How It Works
- Client signs each HTTP request — covering method, path, body digest, nonce
- Signature + metadata sent via
SignatureandSignature-Inputheaders - Server reconstructs signature base, verifies via
ecrecover(EOA) or ERC-1271 (SCA) - Server extracts signer address from
keyidparameter:eip8128:<chain-id>:<address> - No handshake required. Stateless verification.
Security Posture for Git Operations
| Operation | Binding | Replay Protection |
|---|---|---|
git push |
Request-Bound | Non-Replayable |
git fetch (private) |
Request-Bound | Non-Replayable |
git fetch (public) |
No auth required | N/A |
Request-Bound + Non-Replayable is the mandatory baseline per ERC-8128. Any compliant verifier must accept it.
Git Smart HTTP Protocol Mapping
Git over HTTP uses these endpoints:
GET /<repo>/info/refs?service=git-upload-pack— discovery (fetch)GET /<repo>/info/refs?service=git-receive-pack— discovery (push)POST /<repo>/git-upload-pack— fetch packfilePOST /<repo>/git-receive-pack— push packfile
Each request carries its own ERC-8128 signature. The server verifies independently — no session state between requests.
Smart Contract Wallets
Fully supported via ERC-1271. A multisig, a Safe, or any smart contract wallet can push to a repo. The keyid chain parameter determines which chain to verify against.
Client-Side Key Management
TBD — Fran consulting with ERC-8128 authors (Slice team) on recommended patterns. Options include OS keychain, credential helpers, browser wallet delegation.
For the git shim, identity is stored in ~/.repobox/identity and used to sign commits and determine permissions locally.
Server-Side Requirements
- Nonce dedup store (in-memory with TTL or Redis) scoped per
keyid - RPC connection for ERC-1271 verification (smart contract wallets)
- No session store needed
Identity Provider Interface (Internal)
interface IdentityProvider {
type: string; // "evm"
extractIdentity(request): string; // "evm:0x1234..."
verify(request): boolean; // ERC-8128 signature valid?
}
EVM is the only provider at launch. The interface exists so SSH or other providers can be added later without changing the core.
Git Signing Integration
repo.box extends git's native signing infrastructure rather than running alongside it.
How It Works
git repobox initsetsgpg.program = repoboxin the repo's git configuser.signingkeyholds the EVM address:evm:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045- When git needs a signature, it calls
boxwhich signs with the secp256k1 private key - The signature is stored in the commit object — native git, not a side-channel
git verify-commitworks using the EVM address
user.signingkey is the single source of identity. The shim reads it for permission checks. Git reads it for signing. One config key, two purposes.
Key Storage
Private keys are stored in ~/.repobox/keys/, keyed by address:
~/.repobox/keys/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045.key
git repobox keys generate creates a new key pair and prints the address.
git repobox keys import <private-key> imports an existing key.
Identity Precedence
Standard git config resolution, no custom mechanisms:
git -c user.signingkey=evm:0x...(per-command)GIT_CONFIG_*environment variables (per-process).git/configin the repo (per-repo, viagit config --local)~/.gitconfigconditional includes (per-directory pattern)~/.gitconfigglobal default
Multi-Agent Setup
Granting access to a new agent (requires .repobox/config.yml edit permission):
# 1. Generate a key for the agent
git repobox keys generate
# → Created: ~/.repobox/keys/0xCCC...def.key
# → Address: evm:0xCCC...def
# 2. Add the address to .repobox/config.yml (you need edit permission)
# Under groups:
# agents:
# members:
# - evm:0xCCC...def
# 3. Commit the change (only @founders can edit .repobox/config.yml)
git add .repobox/config.yml
git commit -m "onboard agent-1"
Spawning agents with separate identities:
# Each agent process gets its own identity via environment
GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0=user.signingkey \
GIT_CONFIG_VALUE_0=evm:0xCCC...def \
codex --task "fix the auth bug"
The agent uses normal git commands. The shim reads user.signingkey from the environment, checks permissions, and signs commits — all transparently.
Audit trail:
commit a1b2c3d
EVM-signed by evm:0xCCC...def
Author: agent <agent@repo.box>
fix the auth bug
commit e5f6a7b
EVM-signed by evm:0xAAA...789
Author: alice <alice@example.com>
onboard agent-1
Every commit is cryptographically tied to a specific EVM address. You know exactly which agent (or human) made which change.
Security properties:
- Agents cannot grant themselves access (editing .repobox/config.yml requires @founders permission)
- Agents cannot impersonate other identities (signing requires the private key)
- The orchestrator controls onboarding — generates keys, edits .repobox/config.yml, assigns identities
- Post-hoc audit: any commit's authorship is cryptographically verifiable
Local Aliases (Address Book)
Raw EVM addresses are unreadable. Aliases give them human-friendly names — stored locally, not in the repo.
Storage
Aliases live in ~/.repobox/aliases (one per line):
alice = evm:0xAAA...123
claude = evm:0xBBB...456
roudy-piglet = evm:0xCCC...789
This file is per-machine, not per-repo. Different collaborators can have different names for the same addresses. The canonical identity is always the EVM address.
CLI Commands
git repobox alias add alice evm:0xAAA...123
git repobox alias remove alice
git repobox alias list
When setting identity, you can name yourself at the same time:
git repobox identity set <private-key> --alias alice
# → Identity set: @alice (evm:0xAAA...123)
Display
The CLI resolves aliases everywhere — error messages, logs, permission checks:
❌ permission denied: @claude cannot edit .repobox/config.yml
(only @founders can edit .repobox/config.yml on main)
commit a1b2c3d
EVM-signed by @claude (evm:0xBBB...456)
git repobox whoami
# → @alice (evm:0xAAA...123)
If no alias exists for an address, the raw evm:0x... is shown.
Aliases vs Groups
Both use @ prefix but they're different things:
@founders— a group defined in.repobox/config.yml, resolves to multiple addresses@alice— a local alias in~/.repobox/aliases, resolves to one address
The CLI distinguishes by checking the alias file first, then the config groups. Groups always contain multiple members; aliases are always 1:1.
Sub-Agent Naming Convention
When agents spawn sub-agents, they use plus notation for the alias:
@claude+roudy-piglet = evm:0xCCC...789
@claude+swift-otter = evm:0xDDD...012
The parent alias + + + a random adjective-animal name. This makes lineage visible at a glance:
commit f3e2d1c
EVM-signed by @claude+roudy-piglet (evm:0xCCC...789)
refactor database layer
The naming is a convention enforced by tooling (SKILL.md), not by the core system. The alias file just stores whatever string → address mapping you give it.
What Identity Does NOT Cover
- Display names / ENS — UI layer, not identity
- Group membership — See 02-groups.md
- Permissions — See 03-permissions.md
- "Who controls this ENS name?" — Group resolver pattern