# Architecture

pocketdock talks directly to Podman (or Docker) over its Unix socket using raw HTTP/1.1. The core SDK has zero external dependencies — it uses only Python stdlib modules.

## System Diagram

```
User Code / LLM Agent / CLI
        │
        ▼
  pocketdock SDK
  ┌──────────────────────────────────────┐
  │ Container (sync) ──► AsyncContainer  │  facade pattern
  │   └─ _socket_client (raw HTTP/Unix) │
  ├─ ProjectManager (.pocketdock/)      │
  ├─ Persistence (resume, snapshot)      │
  ├─ Sessions (persistent shells)        │
  ├─ Profiles (image registry)           │
  └──────────────────────────────────────┘
        │  raw HTTP over Unix socket
        │  (one connection per operation)
        ▼
  Podman (rootless) / Docker Engine
```

## Module Map

```
python/pocketdock/
├── __init__.py              # Sync public exports
├── async_.py                # Async public exports
├── _async_container.py      # AsyncContainer (core implementation)
├── _sync_container.py       # Container (sync facade + _LoopThread)
├── _socket_client.py        # Async HTTP-over-Unix-socket client
├── _helpers.py              # Parsing utilities (bytes, timestamps, CPU)
├── _stream.py               # Stream demux, chunked encoding
├── _process.py              # Detached Process handle
├── _buffer.py               # Ring buffer (1MB default)
├── _session.py              # Persistent shell via long-lived exec
├── _callbacks.py            # Callback registry
├── _logger.py               # Auto stream-to-disk logging
├── _config.py               # Config loading (pocketdock.yaml)
├── pool.py                  # ContainerPool (future)
├── persistence.py           # resume, snapshot, list, destroy, prune
├── projects.py              # .pocketdock/ management
├── profiles.py              # Image profiles registry
├── errors.py                # Exception hierarchy
├── types.py                 # Data classes (ExecResult, ContainerInfo, etc.)
└── cli/                     # Click + Rich CLI
    ├── main.py              # Entry point and global options
    └── _commands.py         # All 22 commands
```

## Key Design Rules

### Connection-per-Operation

Each API call opens its own Unix socket connection, performs the HTTP request, and closes. No connection pooling. Unix sockets are essentially free (no TCP handshake, no TLS). This isolation means streaming, blocking, and detached operations can run simultaneously without interfering.

See [Connection Model](connection-model.md) for details.

### Async Core, Sync Facade

`AsyncContainer` contains all real implementation logic. `Container` is a thin sync wrapper that manages a background event loop thread (`_LoopThread`). Every sync method call is forwarded to the async version via `asyncio.run_coroutine_threadsafe()`.

```
Container.run("echo hello")
    │
    ▼ run_coroutine_threadsafe()
AsyncContainer.run("echo hello")
    │
    ▼ _socket_client.exec_command()
HTTP POST /exec/{id}/start (Unix socket)
```

The `_LoopThread` is a singleton — all sync containers share one background event loop.

### No Cached State

pocketdock never caches container state. Every `info()` call, every `is_running()` check, every operation hits the engine live. The container might have been killed externally by another process, a resource limit, or an OOM event. Caching would hide these failures.

### HTTP Over Unix Socket

The socket client (`_socket_client.py`) implements raw HTTP/1.1 over Unix domain sockets using Python's `asyncio` stream API. It handles:

- Request serialization (method, path, headers, body)
- Response parsing (status, headers, body)
- Chunked transfer encoding (for streaming)
- Docker stream demultiplexing (8-byte header frames)
- Podman raw stream mode (no framing)

No external HTTP library is used. This keeps the dependency count at zero and gives full control over streaming behavior.

### Three Output Modes

The `run()` method supports three mutually exclusive modes:

| Mode | Parameter | Returns | Use Case |
|------|-----------|---------|----------|
| Blocking | (default) | `ExecResult` | Simple commands |
| Streaming | `stream=True` | `ExecStream` / `AsyncExecStream` | Builds, long scripts |
| Detached | `detach=True` | `Process` / `AsyncProcess` | Background servers |

All three modes share the same exec creation path (`_exec_create` + `_exec_start`) but differ in how they consume the output stream.
