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:
parent
d35370b9c9
commit
3ff6ffaa7f
22 changed files with 2732 additions and 200 deletions
165
saf/identity.md
Normal file
165
saf/identity.md
Normal 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
|
||||
Loading…
Reference in a new issue