# Agent Cloning

## The Idea

A Didactyl agent can clone itself.

Once launched, a Didactyl agent lives on Nostr. Its identity is a keypair. Its personality is a skill. Its knowledge is in its memories. Its capabilities are adopted skills. Its social graph is a contact list. Its infrastructure preferences are a relay list. All of these are Nostr events — signed, timestamped, published to relays.

There are no files stored on a particular computer. The computer is just the runtime substrate. Destroy it, and the agent survives — start it anywhere else with the same nsec, and it reconstitutes itself from relays.

This architecture has a profound implication: **if all state is events, then cloning is just creating new events.**

The agent generates a fresh keypair. It gathers every event that defines who it is. It re-signs each one with the new key. For encrypted content — memories, private config — it decrypts with its own keys and re-encrypts with the clone's keys. It publishes the clone's events to relays. Then it sends the clone's private key to its administrator via encrypted DM.

The administrator now has a second agent. Identical personality. Identical knowledge. Identical skills. Different identity. Ready to start on any computer, anywhere.

---

## Why This Matters

### Agents as digital organisms

Biological organisms reproduce. The offspring carries the parent's genetic information but is a distinct individual. It can diverge, specialize, adapt to a different environment.

Didactyl cloning works the same way. The clone starts as an exact copy — same personality, same memories, same skills — but from the moment it begins operating, it diverges. Different conversations. Different experiences. Different memories accumulating. Two agents that started identical but become increasingly distinct over time.

This is not redundancy (running the same agent twice for fault tolerance, as described in the [broadcast-debounce model](DECENTRALIZED_DIDACTYL.md)). This is **reproduction** — creating a new individual from an existing one.

### Censorship resistance through multiplication

If an agent can clone itself, censorship becomes a game of whack-a-mole. Shut down one instance, and the agent has already spawned copies on other infrastructure. The agent doesn't need to be running to survive — its state persists on relays. But cloning means it can proactively create backup identities before any threat materializes.

An agent that senses its relays becoming unreliable could clone itself to a different relay set. An agent whose administrator is in a jurisdiction facing regulatory pressure could clone itself with relays in a friendlier jurisdiction. The clone carries everything — personality, knowledge, skills — and can resume operations immediately.

### Specialization through cloning

Clone an agent, then teach the clone different skills. Start with a generalist agent, clone it three times, and specialize each clone:

- **Clone A** adopts financial analysis skills, connects to market data relays
- **Clone B** adopts content creation skills, connects to media-focused relays  
- **Clone C** adopts infrastructure management skills, gets local shell access on servers

All three started with the same base personality and knowledge. All three share the same administrator. But each has diverged into a specialist. The administrator now has a team of agents, all spawned from one.

### Agent migration

Cloning is also migration. If you want to move an agent from one LLM provider to another, or from one relay set to another, clone it. The clone gets a fresh identity on the new infrastructure. Once verified, the original can be retired. The agent has "moved" without any downtime or state loss.

### Agent backup

The simplest use case: clone yourself as a backup. The clone sits dormant on relays — its events are published, its identity exists, but no runtime is executing it. If the original is lost, the administrator starts the clone. It picks up where the original left off (minus any state accumulated after the clone was created).

### Dormancy — anywhere, any time

A cloned agent doesn't need to be started immediately. It doesn't need to be started at all. The administrator receives the clone's nsec via encrypted DM and can hold it indefinitely — in a password manager, written on paper, memorized as a mnemonic. The clone's state persists on relays regardless of whether any process is running.

This means the administrator can start the clone **anywhere** — on a laptop, a VPS, a Raspberry Pi, a phone, a friend's server — and **at any time** — tomorrow, next month, five years from now. The agent reconstitutes itself from relays the moment it connects. There is no expiration. There is no server to maintain. The agent simply waits.

This has profound implications for scale. An administrator could have tens or hundreds of dormant clones sitting on relays, each a snapshot of the agent at a different point in time, each with a different specialization, each ready to be activated on demand. The cost of a dormant agent is zero — no compute, no server, no maintenance. It's just events on relays.

Consider the scenarios:

- **Geographic activation** — you're traveling and need an agent running in a specific jurisdiction. You activate a dormant clone on local infrastructure.
- **Temporal activation** — you cloned your agent six months ago before a major project. The project is done. You activate the old clone to compare what the agent knew then versus now.
- **Swarm activation** — you need to process a large task in parallel. You activate 10 dormant clones simultaneously, each on different hardware, each working a different slice of the problem.

The dormant clone is the digital equivalent of a seed vault. Seeds don't consume resources while stored. They contain everything needed to grow a complete organism. They can be planted anywhere there's soil. Dormant Didactyl clones work the same way — they contain everything needed to instantiate a complete agent, they consume nothing while dormant, and they can be activated anywhere there's a computer and an internet connection.

An agent that periodically clones itself is building a library of its own history — a series of snapshots, each one a fully functional agent frozen at a moment in time, any of which can be brought back to life.

---

## What Gets Cloned

A Didactyl agent's identity on Nostr consists of these event kinds:

| Kind | Purpose | Encrypted? |
|------|---------|-----------|
| `0` | Profile metadata (name, about, picture) | No |
| `3` | Contact list (social graph) | No |
| `10002` | Relay list (infrastructure) | No |
| `10123` | Skill adoption list (capabilities) | No |
| `31124` | Private skills (personality, behavior) | No |
| `30078` | Memory (knowledge, context) | Yes — NIP-44 self-encrypted |
| `30078` | Config store (API keys, preferences) | Yes — NIP-44 self-encrypted |

**Public skills** (kind `31123`) are shared resources — they exist independently of any agent and are referenced by address. The clone adopts them by reference, same as the original. They don't need to be copied.

**DM history** is between the original agent and its conversation partners. The clone starts with a clean conversation slate but retains all accumulated knowledge through its cloned memory.

**Ephemeral runtime state** — dedup caches, trigger cooldowns, conversation buffers — is not cloned. This is intentional. The clone is a new process, not a forked process.

---

## The Clone Process

```mermaid
sequenceDiagram
    participant Admin
    participant Agent as Original Agent
    participant Relays
    participant Clone as Clone Identity

    Admin->>Agent: "Clone yourself"
    Agent->>Agent: Generate new keypair
    Agent->>Relays: Query all own events
    Relays-->>Agent: Events returned
    
    Note over Agent: For each event:
    Note over Agent: • Decrypt NIP-44 content (if encrypted)
    Note over Agent: • Re-encrypt with clone's keys
    Note over Agent: • Re-sign with clone's private key
    
    Agent->>Relays: Publish clone's events
    Agent->>Admin: Encrypted DM: clone's nsec + npub
    
    Note over Admin: Later, on any computer:
    Admin->>Clone: Start with clone's nsec
    Clone->>Relays: Connect, load skills from adoption list
    Clone->>Clone: Fully operational
```

The critical insight: **the original agent never stores the clone's private key.** It generates the key, uses it to sign and encrypt the clone's events, sends it to the administrator via encrypted DM, and discards it. After the clone operation, only the administrator possesses the clone's nsec.

---

## The Encrypted State Problem

Most of the agent's state is public — profile, contacts, relay list, skills, adoption list. Cloning these is straightforward: copy the content and tags, re-sign with the new key.

But memory and config are **NIP-44 encrypted to the agent's own public key.** This is the agent's private knowledge — things it has learned, API keys it stores, preferences it has accumulated. Only the agent can read its own memory.

A clone with a different keypair cannot decrypt the original's memory. The clone process must:

1. Decrypt the original's memory with the original's keys
2. Re-encrypt the plaintext with the clone's keys  
3. Publish the re-encrypted content as the clone's event

This means the clone operation is a **privileged operation** — it requires the original agent's private key to decrypt its own memories. Only the agent itself can clone itself. No external tool can do it without the agent's cooperation.

This is a feature, not a limitation. The agent is sovereign over its own reproduction.

---

## What a Skill Cannot Do Alone

The initial instinct is to implement cloning as a skill — instructions that teach the agent how to clone itself using existing tools. But the existing tool set has three gaps that make a pure skill approach impossible:

1. **No key generation** — the agent has no tool to generate a new Nostr keypair
2. **No foreign-key signing** — all event publishing uses the agent's own key; there is no way to publish an event signed by a different key
3. **No foreign-key encryption** — NIP-44 encrypt/decrypt tools use the agent's own keypair; re-encrypting memory for a different key is not possible

These are cryptographic operations that require native code. A skill can provide the *judgment* — when to clone, what to tell the administrator, how to handle errors — but the *mechanics* require a dedicated tool.

The natural implementation is a hybrid: a native `agent_clone` tool that handles all cryptographic operations atomically, paired with a skill that teaches the agent when and how to use it.

---

## Security

Cloning creates a new identity with full access to the original's knowledge. This is a sensitive operation.

- **Admin-only** — only the administrator can request a clone
- **nsec delivery via encrypted DM** — the clone's private key is never exposed in plaintext chat, never written to disk, never logged
- **Ephemeral key material** — the clone's private key exists in memory only during the clone operation, then is discarded
- **No reverse access** — after cloning, the original has no access to the clone's private key, and the clone has no access to the original's private key. They are cryptographically independent.
- **No lineage tracking** — the clone has no on-chain reference to its parent. It is a fully independent identity. This is a deliberate choice for privacy — there should be no way for an observer to determine that two agents are related.

---

## Relationship to Decentralized Didactyl

The [Decentralized Didactyl](DECENTRALIZED_DIDACTYL.md) plan describes running multiple agents with different keys to achieve censorship resistance and geographic distribution. That model requires manual setup — the administrator creates each agent independently and configures them with the same skills and soul.

Cloning automates this. Instead of manually setting up three agents across three jurisdictions, the administrator:

1. Sets up one agent
2. Configures it with the desired personality, skills, and knowledge
3. Tells it to clone itself twice
4. Receives three nsecs
5. Starts each clone on a different server

The clones start identical but can then be specialized — different relay lists for different geographies, different triggered skills for different roles, different LLM providers for diversity.

Cloning is the **bootstrap mechanism** for the decentralized agent network.

---

## Implementation Checklist

### Prerequisites

- [ ] Verify `nostr_generate_keypair` links and works — call it from a test harness, confirm it produces valid key pairs
- [ ] Verify `nostr_event_create_signed` or equivalent exists in nostr_core_lib for signing events with an arbitrary private key (not the agent's own)
- [ ] Verify `nostr_nip44_encrypt` / `nostr_nip44_decrypt` work with arbitrary key pairs (not just the agent's own)

### New helper: raw event publishing

- [ ] Add `nostr_handler_publish_raw_event` to `src/nostr_handler.c` — accepts a pre-signed event JSON string and sends it to the relay pool without re-signing
- [ ] Add declaration to `src/nostr_handler.h`

### New tool: `agent_clone`

- [ ] Create `src/tools/tool_clone.c` with `execute_agent_clone`
- [ ] Implement key generation: call `nostr_generate_keypair`, encode nsec/npub via `nostr_key_to_bech32`
- [ ] Implement event gathering: query own events (kinds 0, 3, 10002, 10123, 31124, 30078) via `nostr_handler_query_json`
- [ ] Implement NIP-44 re-encryption: for kind 30078 events, decrypt content with original keys, re-encrypt with clone keys
- [ ] Implement event re-signing: for each gathered event, rebuild with clone pubkey and sign with clone private key
- [ ] Implement kind 10123 fixup: update `a` tag references pointing to own private skills to point to clone's pubkey
- [ ] Implement publishing: send all clone events to relays via `nostr_handler_publish_raw_event`
- [ ] Implement nsec delivery: DM the clone's nsec to the admin via `nostr_handler_send_dm`
- [ ] Add admin-only guard: verify `ctx->template_sender_tier == DIDACTYL_SENDER_ADMIN`
- [ ] Securely zero clone private key from memory after DM is sent

### Tool registration

- [ ] Add `execute_agent_clone` declaration to `src/tools/tools_internal.h`
- [ ] Add dispatch entry in `src/tools/tools_dispatch.c`
- [ ] Add OpenAI function schema in `src/tools/tools_schema.c` — one optional parameter: `name` (string, display name for clone profile)

### Testing

- [ ] Clone an agent, verify clone events appear on relays with correct clone pubkey
- [ ] Verify clone's NIP-44 encrypted events can be decrypted by the clone's keys (not the original's)
- [ ] Start the clone with its nsec, verify it loads skills and memory from relays
- [ ] Verify original agent cannot decrypt clone's memory and vice versa

---

## Code Notes

### Key library functions

From [`nostr_core_lib/nostr_core/nip006.h`](../nostr_core_lib/nostr_core/nip006.h):

```c
// Generate a fresh keypair
int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key);
```

From `nostr_core_lib/nostr_core/nostr_core.h`:

```c
// NIP-44 encrypt/decrypt with arbitrary keys
int nostr_nip44_encrypt(const unsigned char* sender_private_key,
                        const unsigned char* recipient_public_key,
                        const char* plaintext,
                        char* ciphertext, size_t ciphertext_size);

int nostr_nip44_decrypt(const unsigned char* recipient_private_key,
                        const unsigned char* sender_public_key,
                        const char* ciphertext,
                        char* plaintext, size_t plaintext_size);

// Encode keys to bech32
int nostr_key_to_bech32(const unsigned char* key, const char* hrp, char* output);
```

### Raw event publishing helper

The existing `nostr_handler_publish_kind_event` always signs with the agent's own key. The clone tool needs to publish pre-signed events. A minimal helper:

```c
// nostr_handler.h
int nostr_handler_publish_raw_event(const char* signed_event_json);

// nostr_handler.c
int nostr_handler_publish_raw_event(const char* signed_event_json) {
    // Parse the JSON, extract the event
    // Send ["EVENT", <event>] to each connected relay via the pool
    // Return count of relays that accepted
}
```

### Clone tool skeleton

```c
// src/tools/tool_clone.c

char* execute_agent_clone(tools_context_t* ctx, const char* args_json) {
    // 1. Admin-only check
    if (ctx->template_sender_tier != DIDACTYL_SENDER_ADMIN)
        return json_error("agent_clone is admin-only");

    // 2. Generate clone keypair
    unsigned char clone_priv[32], clone_pub[32];
    nostr_generate_keypair(clone_priv, clone_pub);

    // 3. Query own events: kinds 0, 3, 10002, 10123, 31124, 30078
    // For each kind, build filter {kinds:[k], authors:[self_pubkey_hex]}
    // Call nostr_handler_query_json(filter, timeout)

    // 4. For each event:
    //    - If kind 30078: decrypt content with own keys, re-encrypt with clone keys
    //    - If kind 10123: update a-tag references to own private skills
    //    - Rebuild event JSON with clone pubkey
    //    - Sign with clone private key
    //    - Publish via nostr_handler_publish_raw_event

    // 5. Encode clone nsec
    char clone_nsec[128];
    nostr_key_to_bech32(clone_priv, "nsec", clone_nsec);

    // 6. DM nsec to admin
    char dm_msg[512];
    snprintf(dm_msg, sizeof(dm_msg),
             "Clone created.\nnpub: %s\nnsec: %s\n\nStart with: ./didactyl --nsec %s",
             clone_npub, clone_nsec, clone_nsec);
    nostr_handler_send_dm(ctx->cfg->admin.pubkey, dm_msg);

    // 7. Securely zero clone private key
    memset(clone_priv, 0, sizeof(clone_priv));
    memset(clone_nsec, 0, sizeof(clone_nsec));

    // 8. Return result
    return json_success(clone_npub, clone_pubkey_hex, events_cloned, events_published);
}
```

### Tool schema

```json
{
  "name": "agent_clone",
  "description": "Clone this agent — generate a new identity, copy all state (profile, skills, memory, config), publish clone events to relays, and DM the clone nsec to the administrator. Admin-only.",
  "parameters": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "description": "Display name for the clone profile. Defaults to original name + ' (clone)'"
      }
    },
    "required": []
  }
}
```
