Tailscale Session Recorder Architecture
Comprehensive diagram showing how Tailscale SSH session recording works, including data flow, components, and storage options
Overview
This diagram illustrates the architecture and data flow of Tailscale’s SSH session recording feature, showing how terminal sessions are captured, streamed, and stored securely within a tailnet.
graph TB
subgraph "User Workstation"
U[User] -->|SSH Connection| TC[Tailscale Client]
end
subgraph "Tailscale SSH Server Node"
TC -->|WireGuard Tunnel| TS[Tailscale SSH Server]
TS --> SR[Session Recorder Client]
SR --> TIO[Terminal I/O Wrapper]
TIO --> ASC[Asciinema Formatter]
end
subgraph "Control Plane"
CP[Coordination Server] -.->|ACL Policy| TS
CP -.->|Recorder Config| SR
CP -.->|Node Registry| RN
end
subgraph "Recorder Infrastructure"
subgraph "Primary Recorder Node"
RN[tsrecorder Service]
RN --> API{API Version}
API -->|v2| V2[HTTP/2 Handler]
API -->|v1| V1[HTTP Handler]
V2 --> ACK[Acknowledgment Stream]
V2 --> ST[Storage Handler]
V1 --> ST
end
subgraph "Storage Backends"
ST --> FS[Filesystem Backend]
ST --> S3[S3 Backend]
FS --> DISK[(Local Disk)]
S3 --> CLOUD[(S3/R2/B2)]
end
subgraph "Optional UI"
WEB[Web UI :443] --> RN
WEB --> PLAYER[Asciinema Player]
end
end
subgraph "Failover Recorder Nodes"
RN2[Recorder 2] -.->|Failover| ST
RN3[Recorder 3] -.->|Failover| ST
end
SR -->|Stream Recording| RN
SR -.->|If Primary Fails| RN2
SR -.->|If Secondary Fails| RN3
style U fill:#1976d2,stroke:#fff,stroke-width:2px,color:#fff
style CP fill:#1976d2,stroke:#fff,stroke-width:2px,color:#fff
style RN fill:#388e3c,stroke:#fff,stroke-width:2px,color:#fff
style CLOUD fill:#f57c00,stroke:#fff,stroke-width:2px,color:#fff
style DISK fill:#455a64,stroke:#fff,stroke-width:2px,color:#fff
Data Flow Sequence
sequenceDiagram
participant User
participant SSH Server
participant Recorder Client
participant tsrecorder
participant Storage
User->>SSH Server: SSH Connection Request
SSH Server->>SSH Server: Check ACL Policy
alt Recording Required
SSH Server->>Recorder Client: Start Recording
Recorder Client->>tsrecorder: Probe v2 Support (HEAD /v2/record)
tsrecorder-->>Recorder Client: HTTP 200 (v2 supported)
Recorder Client->>tsrecorder: POST /v2/record (HTTP/2)
Note over Recorder Client,tsrecorder: Send Asciinema Header
tsrecorder->>Storage: Initialize Recording
loop Session Active
SSH Server->>Recorder Client: Terminal Output
Recorder Client->>Recorder Client: Format as Asciinema
Recorder Client->>tsrecorder: Stream Data
tsrecorder->>Storage: Write Data
alt Every 5 seconds (v2)
tsrecorder-->>Recorder Client: Acknowledgment Frame
end
end
User->>SSH Server: Exit Session
Recorder Client->>tsrecorder: Close Stream
tsrecorder->>Storage: Finalize Recording
else Recording Not Required
SSH Server->>User: Normal SSH Session
end
Recording Format Structure
graph LR
subgraph "Asciinema v2 Format"
H[Header JSON] --> |Contains| M[Metadata]
M --> W[Width/Height]
M --> T[Timestamp]
M --> N[Node Info]
M --> U[User Details]
B[Body Lines] --> |Contains| E[Events]
E --> TS[Timestamp]
E --> ET[Event Type]
E --> ED[Event Data]
end
style H fill:#1976d2,stroke:#fff,stroke-width:2px,color:#fff
style B fill:#388e3c,stroke:#fff,stroke-width:2px,color:#fff
ACL Configuration Flow
graph TD
A[SSH Access Rule] --> B{Recording Config?}
B -->|Yes| C[recorders: tag:recorder]
B -->|No| D[No Recording]
C --> E{enforceRecorder?}
E -->|true| F[Fail Closed]
E -->|false| G[Fail Open]
F --> H[Reject if Recording Fails]
G --> I[Allow Even if Recording Fails]
style A fill:#1976d2,stroke:#fff,stroke-width:2px,color:#fff
style F fill:#d32f2f,stroke:#fff,stroke-width:2px,color:#fff
style G fill:#f57c00,stroke:#fff,stroke-width:2px,color:#fff
Storage Backend Options
graph TD
R[tsrecorder] --> SH{Storage Handler}
SH --> FS[Filesystem Backend]
SH --> S3[S3 Backend]
FS --> FP[File Path Structure]
FP --> |Format| FSS[basePath/NodeID/timestamp.cast]
S3 --> S3C{S3 Provider}
S3C --> AWS[AWS S3]
S3C --> CF[Cloudflare R2]
S3C --> BB[Backblaze B2]
S3C --> GCS[Google Cloud Storage]
S3C --> MIN[MinIO]
style R fill:#388e3c,stroke:#fff,stroke-width:2px,color:#fff
style AWS fill:#f57c00,stroke:#fff,stroke-width:2px,color:#fff
style CF fill:#f57c00,stroke:#fff,stroke-width:2px,color:#fff
Failure Handling States
stateDiagram-v2
[*] --> Connecting: SSH Session Start
Connecting --> CheckPolicy: Validate ACL
CheckPolicy --> NoRecording: Recording Not Required
CheckPolicy --> FindRecorder: Recording Required
FindRecorder --> TryPrimary: Get Recorder IPs
TryPrimary --> Connected: Success
TryPrimary --> TryFailover: Connection Failed
TryFailover --> Connected: Failover Success
TryFailover --> RecordingFailed: All Recorders Down
RecordingFailed --> SessionRejected: enforceRecorder=true
RecordingFailed --> SessionAllowed: enforceRecorder=false
Connected --> Streaming: Start Recording
Streaming --> SessionComplete: Normal Exit
Streaming --> ConnectionLost: Recorder Down
ConnectionLost --> SessionTerminated: enforceRecorder=true
ConnectionLost --> ContinueUnrecorded: enforceRecorder=false
NoRecording --> SessionComplete
SessionAllowed --> SessionComplete
ContinueUnrecorded --> SessionComplete
SessionComplete --> [*]
SessionRejected --> [*]
SessionTerminated --> [*]
Customer-Facing Overview
graph LR
subgraph "Your Organization"
subgraph "Users"
U1[π€ Developer]
U2[π€ Admin]
U3[π€ Support]
end
subgraph "Your Servers"
S1[π₯οΈ Production Server]
S2[π₯οΈ Database Server]
S3[π₯οΈ Web Server]
end
subgraph "Your Recording Infrastructure"
R[πΉ Recorder Node]
ST[(πΎ Storage)]
R --> ST
end
end
U1 & U2 & U3 -.->|π Secure SSH| S1 & S2 & S3
S1 & S2 & S3 -->|π Session Recording| R
subgraph "Benefits"
B1[β
Compliance]
B2[π Audit Trail]
B3[π‘οΈ Security]
B4[π― Troubleshooting]
end
ST -.-> B1 & B2 & B3 & B4
style U1 fill:#1976d2,stroke:#000,stroke-width:2px,color:#fff
style U2 fill:#1976d2,stroke:#000,stroke-width:2px,color:#fff
style U3 fill:#1976d2,stroke:#000,stroke-width:2px,color:#fff
style R fill:#388e3c,stroke:#000,stroke-width:2px,color:#fff
style ST fill:#f57c00,stroke:#000,stroke-width:2px,color:#fff
style B1 fill:#388e3c,stroke:#fff,stroke-width:2px,color:#fff
style B2 fill:#388e3c,stroke:#fff,stroke-width:2px,color:#fff
style B3 fill:#388e3c,stroke:#fff,stroke-width:2px,color:#fff
style B4 fill:#388e3c,stroke:#fff,stroke-width:2px,color:#fff
How Session Recording Works (Simplified)
graph TD
A[1. User connects via SSH] --> B[2. System checks policy]
B --> C{Recording enabled?}
C -->|Yes| D[3. Start recording session]
C -->|No| E[Regular SSH session]
D --> F[4. Capture terminal output]
F --> G[5. Stream to recorder]
G --> H[6. Save to storage]
H --> I[7. Available for review]
style A fill:#1976d2,stroke:#000,stroke-width:2px,color:#fff
style D fill:#388e3c,stroke:#000,stroke-width:2px,color:#fff
style G fill:#f57c00,stroke:#000,stroke-width:2px,color:#fff
style I fill:#7b1fa2,stroke:#000,stroke-width:2px,color:#fff
Setting Up Recording (3 Easy Steps)
graph LR
subgraph "Step 1: Deploy Recorder"
D1[π³ Docker Container]
D2[βΈοΈ Kubernetes]
D3[π₯οΈ VM/Server]
D1 & D2 & D3 --> R1[Recorder Running]
end
subgraph "Step 2: Configure Storage"
R1 --> S1[πΎ Local Disk]
R1 --> S2[βοΈ AWS S3]
R1 --> S3[π Other S3]
end
subgraph "Step 3: Update ACL Policy"
P[π ACL Policy] --> |Define| WHO[Who gets recorded]
P --> |Define| WHERE[Which servers]
P --> |Define| HOW[Storage location]
end
style R1 fill:#388e3c,stroke:#000,stroke-width:2px,color:#fff
style P fill:#1976d2,stroke:#000,stroke-width:2px,color:#fff
Privacy & Security Guarantees
graph TD
subgraph "What We See"
T1[β Nothing]
T2[Your data never leaves your network]
end
subgraph "What You Control"
Y1[β
All recordings]
Y2[β
Storage location]
Y3[β
Access policies]
Y4[β
Retention rules]
end
subgraph "Security Features"
S1[π End-to-end encrypted]
S2[π‘οΈ Zero-trust access]
S3[π Audit logging]
S4[π« No passwords recorded]
end
style T1 fill:#d32f2f,stroke:#000,stroke-width:2px,color:#fff
style Y1 fill:#388e3c,stroke:#000,stroke-width:2px,color:#fff
style Y2 fill:#388e3c,stroke:#000,stroke-width:2px,color:#fff
style Y3 fill:#388e3c,stroke:#000,stroke-width:2px,color:#fff
style Y4 fill:#388e3c,stroke:#000,stroke-width:2px,color:#fff
Common Use Cases
graph TD
subgraph "Compliance & Audit"
C1[π SOC2/ISO27001]
C2[ποΈ Regulatory Requirements]
C3[π Internal Policies]
end
subgraph "Security Operations"
S1[π Incident Investigation]
S2[π¨ Threat Detection]
S3[π Activity Monitoring]
end
subgraph "Operations & Support"
O1[π Troubleshooting]
O2[π Training & Documentation]
O3[π€ Vendor Support]
end
R[Session Recordings] --> C1 & C2 & C3
R --> S1 & S2 & S3
R --> O1 & O2 & O3
style R fill:#388e3c,stroke:#000,stroke-width:2px,color:#fff
style C1 fill:#1976d2,stroke:#fff,stroke-width:2px,color:#fff
style S1 fill:#f57c00,stroke:#fff,stroke-width:2px,color:#fff
style O1 fill:#7b1fa2,stroke:#fff,stroke-width:2px,color:#fff
Key Features
- End-to-End Encryption: All recordings streamed over WireGuard-encrypted tailnet connections
- Privacy by Design: Recordings stored on user-controlled infrastructure, never visible to Tailscale
- Flexible Storage: Support for local filesystem or S3-compatible object storage
- High Availability: Multiple recorder nodes with automatic failover
- Compliance Ready: Asciinema format allows searching and auditing of sessions
- Access Control: Fine-grained ACL policies determine which sessions are recorded
- Fail-Safe Options: Configurable behavior when recording infrastructure is unavailable