Watch
1
0
Fork
You've already forked souveraine
0
souveraine/saf/identity.md
Fimeg 3ff6ffaa7f license: relicense AGPL-3.0; bring SAF docs online; keep CLAUDE.md + docs/ local
SAF (souveraine architecture files) is now the in-repo doc set. working notes
(CLAUDE.md, docs/) stay on disk, gitignored.
2026-06-19 10:17:56 -04:00

6.5 KiB

SAF: Identity & Memory

How agents know who they are, and how they remember. Last updated: 2026-05-12 (Full audit — seed identity, MemoryRepo, subconscious agents)


Seed Identity (Ed25519)

File: src/core/identity/seed.rs (187 lines, 5 tests) Status: Working

Every agent gets a per-agent Ed25519 keypair at ~/.souveraine/agents/{uuid}/seed/:

  • Loaded or generated on first access (SeedId::load_or_generate)
  • Private key stored at private.key (0600 permissions on Unix)
  • Public key at public.key
  • Methods: sign(), verify(), public_key_hex(), glyph() (4-character geometric-shapes rendering)
  • Standalone glyph_from_pubkey() for remote agents where only the pubkey is known

The seed directory lives alongside the memfs (agents/{uuid}/seed/) so the agent's identity travels with its memory — federation can later sync this directory as one unit.

CLI Subcommands

souveraine identity show          # Show public key + glyph
souveraine identity sign --message <text>     # Sign a message
souveraine identity verify --message <text> --signature <hex>  # Verify

Integration

  • AgentInventory creates the per-agent seed during agent creation (create() method)
  • Wired into LocalBackend via seed_id: Arc<SeedId>
  • SensorEvent.seed_id is pre-wired for federation — None means local, Some(pubkey_hex) means federated

Per-Agent Storage Layout

~/.souveraine/
├── server/
│   ├── agents/{uuid}/           Server-managed dir
│   │   ├── agent.json           Agent metadata
│   │   └── conversations/       Per-conversation JSON
│   └── database.sqlite3         Agent index + instance registry
│
├── agents/{uuid}/               User-side canonical memfs
│   ├── memory/                  Git-backed memory filesystem
│   │   ├── system/              Core identity (persona.md, human.md, covenant/, state.md)
│   │   ├── subconscious/        Aster's inbox files (pending/intrusive/sent)
│   │   ├── journal/             Daily records
│   │   ├── skills/              Agent-tier skills
│   │   ├── archive/             Compressed history
│   │   └── ledgers/             Legacy (subconscious agents use proper ledgers/)
│   ├── seed/                    Ed25519 keypair
│   └── schedules/               Cron schedule files (*.md with YAML frontmatter)
│
└── subconscious-agents/{id}-sub/
    └── memory.git/              Subconscious agent's memfs
        ├── system/
        │   ├── persona.md       Aster's identity prompt
        │   └── subconscious.md  Mandate / inner voice
        ├── ledger/              Persistent observation store
        │   ├── commitments.md   Promises made by the primary
        │   ├── assumptions.md   Unverified beliefs in play
        │   ├── patterns.md      Recurring behaviors
        │   ├── drift_log.md     Intention/action mismatches
        │   ├── relationships.md Tone shifts, trust signals
        │   └── infrastructure.md System errors, resource constraints
        └── inbox/               (reserved for future use)

Memory System (MemoryRepo)

File: src/core/memory/mod.rs (1032 lines, 10 tests) Status: Working

Every agent has a git repository at ~/.souveraine/agents/{id}/memory/. Git is used as a versioned filesystem:

MemoryRepo {
    agent_id: String,
    root: PathBuf,
    auto_commit: bool,
}

Operations

Method Status Description
new() Init or open git repo at {base}/{id}/memory/
new_default() Uses ~/.souveraine/agents/{id}/memory/
open() Open at explicit path (for subconscious agents, nonstandard layouts)
init() Initialize git repo, create system/ with persona.md + state.md, initial commit
init_subconscious_ledger() Create ledger files with frontmatter, idempotent
read(path) Read file, parse frontmatter, return body
write(path, content) Write file + auto-commit, enforce read_only + limit
append(path, content) Append to file + auto-commit, enforce read_only + limit
list(subdir) List files in memory directory (skips .git)
status() Git status: last commit, uncommitted changes, remote URL
delete(path) Delete file + auto-commit, enforce read_only
commit(paths, message) Git commit specific paths

Frontmatter

Every memory file requires YAML frontmatter:

---
description: Purpose of this file (required)
read_only: true              # optional, blocks writes
tags: ["system", "core"]    # optional
limit: 4000                  # optional, max body chars (LET-8133 closure)
---
Body content here...

Memory Tool

The memory tool exposes this as a unified subcommand interface:

memory read system/persona
memory write system/persona "new content"
memory append journal/2026-05-06 "new entry"
memory ls system/
memory init
memory status
memory compact --strategy sliding-window
memory delete system/state

System Prompt Assembly

File: src/core/prompt.rs (408 lines, 7 tests) Status: Working

The build_system_prompt() function reads the agent's memfs and assembles a system message:

  1. Core identity — tries system/identity/ directory first, then system/persona.md, then system/persona/identity.md
  2. Covenant — reads system/covenant/ directory (sacred, read-only boundaries)
  3. Human context — reads system/human/ directory or system/human.md
  4. State — reads system/state.md
  5. Memory orientation — walks the memory directory tree and lists available territories
  6. Skills — injects skill listings from the 4-tier registry

The build_aster_prompt() function builds the subconscious prompt from the subconscious agent's memfs:

  1. Identity from system/persona.md
  2. Mandate from system/subconscious.md
  3. Ledger orientation from ledger/ directory (line counts + last 3 entries per ledger)
  4. Falls back to empty string (caller uses hardcoded default if files don't exist)

Name ↔ UUID Mapping

Current: UUID-based (agents created with UUID, stored in SQLite, directories at agents/{uuid}/) CLI: Resolves by name → UUID at conversation start Missing: No backwards-compat shim for the old name-based layout, no souveraine agents rename command