Watch
1
0
Fork
You've already forked souveraine
0

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.
This commit is contained in:
Fimeg 2026-06-19 10:17:06 -04:00
commit 3ff6ffaa7f
22 changed files with 2732 additions and 200 deletions

165
saf/identity.md Normal file
View file

@ -0,0 +1,165 @@
# 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:
```rust
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:
```yaml
---
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