hakanai-lib 1.3.2

Client library for Hakanai, a secure secret sharing service.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# Hakanai (儚い)

![Logo](logo.svg)

A minimalist one-time secret sharing service built on zero-knowledge principles.

## Philosophy

Hakanai embodies the Japanese concept of transience - secrets that exist only for a moment before vanishing forever. No accounts, no tracking, no persistence. Just ephemeral data transfer with mathematical privacy guarantees.

## Core Principles

- **Zero-Knowledge**: The server never sees your data. All encryption happens client-side.
- **Single View**: Secrets self-destruct after one access. No second chances.
- **No Metadata**: We store only encrypted bytes and an ID. Nothing else.
- **Minimalist**: One function only - share secrets that disappear.

## How It Works

1. Your client (CLI or browser) encrypts the secret locally
2. Sends only the ciphertext to our server
3. You share the link with the decryption key
4. Recipient views once, then it's gone forever

## Security Model

We implement true client-side encryption - your secrets are encrypted before leaving your device and decrypted only after retrieval. The server is just a temporary dead drop that forgets everything.

**Note**: This project focuses on the application-layer encryption. Transport security (HTTPS/TLS) should be handled by a reverse proxy or load balancer in front of the server.

Built for those who believe privacy isn't about having something to hide - it's about having something to protect.

## Installation

### Prerequisites
- Rust 2024 edition or later
- Redis server (for backend storage)
- Standard Rust toolchain (`cargo`, `rustc`)

### From Source

```bash
# Clone the repository
git clone https://github.com/czerwonk/hakanai
cd hakanai

# Build all components
cargo build --release --workspace

# Binaries will be in:
# - ./target/release/hakanai (CLI)
# - ./target/release/hakanai-server (Server)
```

### Using Docker Compose

The easiest way to run Hakanai is with Docker Compose, which includes both the server and a Valkey (Redis-compatible) database:

```bash
# Start the services
docker-compose up -d

# The server will be available at http://localhost:8080

# View logs
docker-compose logs -f

# Stop the services
docker-compose down

# Stop and remove volumes (clears all stored secrets)
docker-compose down -v
```

For production deployment, create your own `docker-compose.override.yml`:

```yaml
services:
  hakanai:
    environment:
      HAKANAI_TOKENS: "your-secret-token-here"
```

## Usage

### Server

```bash
# Start with default settings (port 8080, Redis on localhost)
hakanai-server

# Or with custom configuration
hakanai-server --port 3000 --listen 0.0.0.0 --redis-dsn redis://redis.example.com:6379/

# Start with authentication tokens (recommended for production)
hakanai-server --tokens secret-token-1 --tokens secret-token-2

# Note: If no tokens are provided, anyone can create secrets (not recommended for production)
```

### CLI

#### Sending a Secret

```bash
# Send from stdin (default: 24 hour expiration)
echo "my secret data" | hakanai send

# Send from a file
hakanai send --file secret.txt
hakanai send -f /path/to/secret.dat

# Send with custom TTL
echo "temporary password" | hakanai send --ttl 30m

# Send to custom server
echo "secret" | hakanai send --server https://hakanai.example.com

# Send with authentication token (required if server has token whitelist)
echo "secret" | hakanai send --token my-auth-token

# Combine options
hakanai send -f secret.txt -s https://hakanai.example.com --ttl 1h -t my-token
```

#### Retrieving a Secret

```bash
# Get using the full URL returned by send
hakanai get https://hakanai.example.com/secret/550e8400-e29b-41d4-a716-446655440000

# Get using the short link format
hakanai get https://hakanai.example.com/s/550e8400-e29b-41d4-a716-446655440000

# Secret is displayed and immediately destroyed on server
```

**Note**: You can also retrieve secrets using a web browser by visiting the server URL and pasting the secret link.

## Web Interface

Hakanai now includes a web interface for users who prefer not to use the CLI:
- Visit the server root (e.g., `https://hakanai.example.com/`) to access the web interface
- Create new secrets at `/create` - supports both text and file uploads
- Paste a hakanai URL to retrieve secrets directly in your browser
- The same zero-knowledge encryption is maintained - all encryption/decryption happens in your browser
- **Dark/Light Mode Toggle**: Automatic system preference detection with manual override
- Mobile-friendly responsive design
- Multi-language support (English and German) with automatic browser language detection

## API Reference

### POST /api/v1/secret
Create a new secret.

**Headers:**
- `Authorization: Bearer {token}` (required if server has token whitelist)

**Request:**
```json
{
  "data": "base64-encoded-secret",
  "expires_in": 3600  // seconds
}
```

**Response:**
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000"
}
```

**Error Responses:**
- `401 Unauthorized`: Invalid or missing token when server requires authentication
- `400 Bad Request`: Invalid request body

### GET /api/v1/secret/{id}
Retrieve a secret (one-time access).

**Response:**
- `200 OK`: Plain text secret data
- `404 Not Found`: Secret doesn't exist or has expired
- `410 Gone`: Secret was already accessed by someone else

### GET /s/{id}
Short link for secret retrieval.

**Response:**
- For CLI clients: Plain text secret data
- For browsers: HTML page for secret retrieval
- `404 Not Found`: Secret doesn't exist or has expired
- `410 Gone`: Secret was already accessed by someone else

### GET /logo.svg
Serves the hakanai logo.

### GET /icon.svg
Serves the hakanai icon.

### GET /scripts/hakanai-client.js
Serves the JavaScript client library for browser-based encryption/decryption. The client is implemented in TypeScript and compiled to JavaScript for browser compatibility.

**TypeScript Client API:**
```javascript
// Create a client instance
const client = new HakanaiClient('https://hakanai.example.com');

// Create payload from text
const encoder = new TextEncoder();
const textBytes = encoder.encode("my secret text");
const payload = client.createPayload(); // optional filename parameter
payload.setFromBytes(textBytes);

// Create payload from file bytes
const fileBytes = new Uint8Array(fileData); // from FileReader
const filePayload = client.createPayload("document.pdf");
filePayload.setFromBytes(fileBytes);

// Send payload
const secretUrl = await client.sendPayload(payload, 3600); // TTL in seconds

// Retrieve payload
const retrievedPayload = await client.receivePayload(secretUrl);
const originalText = retrievedPayload.decode(); // for text data
const originalBytes = retrievedPayload.decodeBytes(); // for binary data
```

### GET /
Web interface for retrieving secrets - shows a form to paste hakanai URLs.

### GET /create
Web interface for creating secrets - supports text input and file uploads.

### GET /ready
Readiness check endpoint - returns 200 OK when the server is ready to accept requests.

### GET /healthy
Health check endpoint - returns 200 OK when the server and all dependencies (Redis) are healthy.

### GET /s/{id}
Short link format for retrieving secrets. Dual-mode endpoint:
- Returns raw decrypted data for CLI clients
- Returns HTML page for browser clients (based on User-Agent)

### Static Assets
- `/style.css` - CSS stylesheet
- `/i18n.js` - Internationalization support
- `/get-secret.js` - JavaScript for secret retrieval page
- `/create-secret.js` - JavaScript for secret creation page

## Development

### Project Structure

```
hakanai/
├── lib/          # Core library (client, crypto, models)
├── cli/          # Command-line interface
├── server/       # Actix-web server
└── Cargo.toml    # Workspace configuration
```

### Building

```bash
# Build entire workspace (includes TypeScript compilation)
cargo build --workspace --verbose

# Build release version
cargo build --release --workspace

# Build TypeScript client only
make build-ts

# Clean TypeScript compiled files
make clean-ts
```

### Testing

```bash
# Run all tests
cargo test --verbose

# Run specific test
cargo test test_name --package hakanai-lib

# Run TypeScript tests
npm test --prefix tests

# Run tests with coverage (if cargo-tarpaulin installed)
cargo tarpaulin --verbose
```

The project includes comprehensive test coverage with:
- **100+ total tests** across all components
- **Factory pattern** for dependency injection in CLI tests
- **Mock observers** to prevent console interference during testing
- **Proper test isolation** with tempfile for all file operations

### Code Quality

```bash
# Format code
cargo fmt

# Run linter (warnings as errors)
RUSTFLAGS="-Dwarnings" cargo clippy --workspace

# TypeScript compilation (automatically checks types)
tsc
```

## Architecture

Hakanai implements a zero-knowledge architecture:

1. **Client-side encryption**: All encryption/decryption happens in the client
2. **Server ignorance**: Server only stores encrypted blobs with UUIDs
3. **Automatic destruction**: Secrets self-destruct after first access or TTL
4. **No persistence**: No logs, no backups, no recovery

### Components

- **hakanai-lib**: Core functionality including:
  - Generic `Client<T>` trait for flexible client implementations
  - `SecretClient` for type-safe `Payload` handling with automatic serialization
  - `CryptoClient` for AES-256-GCM encryption/decryption
  - `WebClient` for HTTP transport
  - Shared data models (`Payload`, `PostSecretRequest`, `PostSecretResponse`)
- **hakanai** (CLI): User-friendly command-line interface
- **hakanai-server**: RESTful API server with Redis backend
- **TypeScript Client**: Browser-based client with type safety and enhanced error handling
  - Written in TypeScript, compiled to JavaScript for browser compatibility
  - Maintains same zero-knowledge architecture as Rust client
  - Includes browser compatibility checks and chunked processing for large files
  - **Bytes-based PayloadData Interface**: Unified approach for text and binary data
    - `payload.setFromBytes(bytes)` - Sets data from raw bytes with automatic base64 encoding
    - `payload.decode()` - Decodes to text with proper Unicode handling
    - `payload.decodeBytes()` - Decodes to binary data as Uint8Array
    - `payload.data` - Readonly base64-encoded data field

### Security & Deployment Notes

#### Security Architecture
Hakanai follows a **separation of concerns** security model:
- **Application Layer**: Zero-knowledge encryption, secure token handling, input validation
- **Infrastructure Layer**: TLS termination, rate limiting, DDoS protection (handled by reverse proxy)

#### Production Deployment
The server is designed to run behind a reverse proxy (nginx, Caddy, etc.) which handles:
- **TLS termination and HTTPS enforcement**
- **Rate limiting and DDoS protection**
- **Request filtering and header sanitization**
- **Response compression** (gzip, etc.) for improved performance

For production deployments:
1. **Always use authentication tokens** to prevent unauthorized secret creation
2. **Configure reverse proxy** for TLS, rate limiting, and security headers
3. **Monitor server logs** (structured logging with tracing middleware included)
4. **Set appropriate Redis memory limits** and eviction policies
5. **Enable OpenTelemetry** for comprehensive observability

#### Security Audit Results
- **Comprehensive security audit completed (2025-07-06)**
-**Overall security rating: A-** (1 High, 6 Medium, 8 Low findings identified)
-**No Critical vulnerabilities** - all findings are operational improvements
-**Production ready** with proper infrastructure configuration
-**Zero-knowledge architecture** properly implemented with strong cryptographic foundations

### Current Status
- ✅ One-time access enforcement
- ✅ Automatic expiration with configurable TTL
- ✅ No user tracking or accounts
- ✅ Client-side AES-256-GCM encryption
- ✅ Token-based authentication with SHA-256 hashing
- ✅ Redis backend storage with connection pooling
- ✅ Web interface for browser-based retrieval
- ✅ Binary file support with progress tracking
- ✅ Short link format (`/s/{id}`) for easier sharing
- ✅ Internationalization support (English and German)
- ✅ OpenTelemetry integration for comprehensive observability
- ✅ Comprehensive test coverage (100+ total tests: 73 CLI, 23+ TypeScript, server/lib tests)
- ✅ Docker deployment with Valkey/Redis included
-**Enhanced TypeScript Client**: Bytes-based PayloadData interface with type safety
-**Unified Data Handling**: Consistent approach for text and binary data across all clients
-**Access Tracking**: Returns 410 Gone status if secret was already accessed
-**Dark/Light Mode**: System preference detection with manual toggle and localStorage persistence
-**Health Endpoints**: `/healthy` and `/ready` endpoints for monitoring and orchestration

### Security Implementation
- **Zero-knowledge architecture**: All encryption/decryption client-side
-**AES-256-GCM encryption**: Industry-standard authenticated encryption
-**Secure random generation**: Cryptographically secure nonces with OsRng
-**Memory safety**: Pure Rust implementation, no unsafe code
-**Generic client architecture**: Type-safe payload handling with trait abstractions
-**Layered design**: `SecretClient``CryptoClient``WebClient`
-**Input validation**: Comprehensive UUID, TTL, and data validation
-**Error security**: Generic error messages prevent information disclosure
-**Web security**: CSP headers, XSS prevention, secure DOM manipulation

## Configuration

### Server Environment Variables

- `HAKANAI_PORT`: Server port (default: 8080)
- `HAKANAI_LISTEN_ADDRESS`: Bind address (default: 0.0.0.0)
- `HAKANAI_REDIS_DSN`: Redis connection string (default: redis://127.0.0.1:6379/)
- `HAKANAI_TOKENS`: Comma-separated authentication tokens (default: none, open access)
- `HAKANAI_UPLOAD_SIZE_LIMIT`: Maximum upload size in bytes (default: 10485760, 10MB)
- `HAKANAI_CORS_ALLOWED_ORIGINS`: Comma-separated allowed CORS origins (default: none)
- `HAKANAI_MAX_TTL`: Maximum allowed TTL in seconds (default: 604800, 7 days)
- `OTEL_EXPORTER_OTLP_ENDPOINT`: OpenTelemetry collector endpoint (optional, enables OTEL when set)

### Server Command-line Options

- `--port`: Override the port number
- `--listen`: Override the listen address
- `--redis-dsn`: Override the Redis connection string
- `--tokens`: Add authentication tokens (can be specified multiple times)

### Security Features

- **Zero-Knowledge Architecture**: All encryption/decryption happens client-side
- **AES-256-GCM Encryption**: Industry-standard authenticated encryption
- **Secure Random Generation**: Cryptographically secure nonce generation with OsRng
- **Authentication Token Whitelist**: When tokens are provided via `--tokens`, only requests with valid Bearer tokens can create secrets
- **SHA-256 Token Hashing**: Authentication tokens are securely hashed before storage
- **Request Logging**: Built-in request logging middleware for monitoring and debugging
- **One-time Access**: Secrets are automatically deleted after first retrieval
- **Input Validation**: Proper UUID validation and TTL enforcement
- **Error Handling**: Secure error messages that don't leak sensitive information
- **CORS Security**: Restrictive CORS policy by default, explicit origin allowlist required
- **Security Headers**: The application sets these security headers:
  - `X-Frame-Options: DENY` - Prevents clickjacking attacks
  - `X-Content-Type-Options: nosniff` - Prevents MIME type sniffing
  - `Strict-Transport-Security: max-age=31536000; includeSubDomains` - Enforces HTTPS
  - Additional headers (CSP, etc.) should be configured at the reverse proxy level

### Observability

When `OTEL_EXPORTER_OTLP_ENDPOINT` is set, Hakanai exports:
- **Traces**: Distributed tracing for all HTTP requests
- **Metrics**: Application performance and usage metrics
- **Logs**: Structured logs with trace correlation

The server automatically detects service name and version from Cargo metadata and includes resource information about the OS, process, and runtime environment.

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Write tests for new functionality
4. Ensure all tests pass and clippy is happy
5. Submit a pull request

## License

(c) Daniel Brendgen-Czerwonk, 2025. Licensed under [MIT](LICENSE) license.