dev_agent/src/tantoc2_dev_agent/. It implements every interface described here.Architecture#
An agent package consists of three server-side components and one client-side component:
graph TB
subgraph "Server-Side (Python Plugins)"
AP[AgentPackageBase] --> CP[CryptoProvider]
AP --> PC[ProtocolCodec]
AP --> BT[Build Templates]
AP --> CAP[Capability Declarations]
end
subgraph "Client-Side (Your Agent Binary)"
Agent[Agent Binary] --> CC[Crypto Implementation]
Agent --> WP[Wire Protocol]
Agent --> CMD[Built-in Commands]
Agent --> ML[Module Loader]
end
AP -.->|"stamp()"| Agent
The server-side plugins handle registration, decryption, decoding, and building. The client-side agent handles communication, command execution, and module loading.
Step 1: CryptoProvider#
The CryptoProvider handles key exchange and message encryption. Implement CryptoProviderBase:
| |
CryptoSession#
The CryptoSession dataclass holds per-agent session state:
| |
session_data is where you store your session keys, counters, nonces, etc. It’s persisted in the pipeline for the lifetime of the session and serialized to the database as JSON.
Thread Safety#
encrypt() and decrypt() may be called from multiple threads with the same session. If you use counters or mutable state in session_data, ensure atomic updates.
Step 2: ProtocolCodec#
The ProtocolCodec translates between wire bytes and InternalMessage:
| |
InternalMessage#
All agent communication passes through this canonical format:
| |
Your codec must map your wire format’s message types to MessageType values.
Step 3: AgentPackage#
The AgentPackageBase ties everything together:
| |
Build Dataclasses#
| |
Step 4: Wire Protocol#
All agent packages use this header:
| |
- Magic bytes (4B): Routes to the correct CryptoProvider and ProtocolCodec
- Session token (16B): All zeros for registration; assigned token thereafter
- Payload: Everything after the header is passed to
CryptoProvider.decrypt()(orcreate_session()for registration)
Step 5: Agent-Side Implementation#
Your agent binary must implement:
Registration#
- Generate a keypair
- Send:
[magic] + [null_token (16 zeros)] + [public_key + codec(register_msg)] - Receive:
[magic] + [session_token] + [server_public_key + codec(register_response)] - Derive shared session key using the server’s public key
- Store session token for future messages
Check-in Loop#
| |
Task Execution#
Handle standard task types:
| Task Type | Expected Behavior |
|---|---|
survey | Return system info (hostname, OS, arch, username, IPs) |
beacon_config | Update check-in interval and jitter |
kill | Self-destruct (zero memory, remove persistence, exit) |
upload | Receive file content, write to disk |
download | Read file, return content |
load_module | Load and execute a compiled payload |
unload_module | Clean up a managed module |
Module Loading (if supported)#
For agents that support module loading:
- Managed mode (
daemonize=false): Load payload, execute, return results through your check-in channel - Daemonized mode (
daemonize=true): Launch payload independently; if it’s an agent, it registers on its own
P2P Relay (if supported)#
For agents that support relay:
- Listen on a local port for interior agent connections
- Forward their raw wire bytes to the teamserver as
relay_forwardmessages - Buffer and forward responses back to interior agents
Step 6: Packaging#
File Structure#
| |
Entry Points (pyproject.toml)#
| |
Installation#
| |
Reference: Dev Agent#
The dev agent (dev_agent/src/tantoc2_dev_agent/) is the complete reference implementation:
| File | Component | Details |
|---|---|---|
package.py | AgentPackageBase | Magic: \xde\xad\xc2\x01, two templates (beacon/session), full stamping logic |
crypto.py | CryptoProviderBase | ECDH P-256 + HKDF-SHA256 + AES-256-GCM, counter-based anti-replay |
codec.py | ProtocolCodecBase | JSON + zlib compression |
agent.py | Client agent | Beacon/session modes, built-in commands, module loading, P2P relay |
template.py | Build stamping | Python source patching with encrypted config embedding |
Checklist#
Before deploying your agent package:
-
magic_bytes()returns exactly 4 bytes, unique across all packages -
crypto_provider_name()matches your CryptoProvider’splugin_name() -
protocol_codec_name()matches your ProtocolCodec’splugin_name() - Registration handshake completes successfully
- Encrypt/decrypt roundtrip produces correct plaintext
- Codec encode/decode roundtrip preserves all InternalMessage fields
- Anti-replay rejects duplicate messages
- Kill date is enforced by the agent
-
stamp()produces a working binary with embedded config - Entry points are correctly declared in pyproject.toml
-
capabilities()accurately reflects what the agent supports