# Stegasoo REST API Documentation (v4.0.2)

Complete REST API reference for Stegasoo steganography operations.

## Table of Contents

- [Overview](#overview)
- [What's New in v4.0.0](#whats-new-in-v400)
- [Installation](#installation)
- [Base URL](#base-url)
- [Endpoints](#endpoints)
  - [GET /](#get--status)
  - [GET /modes](#get-modes)
  - [GET /channel/status](#get-channelstatus)
  - [POST /channel/generate](#post-channelgenerate)
  - [POST /channel/set](#post-channelset)
  - [DELETE /channel](#delete-channel)
  - [POST /generate](#post-generate)
  - [POST /encode](#post-encode-json)
  - [POST /encode/file](#post-encodefile)
  - [POST /encode/multipart](#post-encodemultipart)
  - [POST /decode](#post-decode-json)
  - [POST /decode/multipart](#post-decodemultipart)
  - [POST /compare](#post-compare)
  - [POST /will-fit](#post-will-fit)
  - [POST /image/info](#post-imageinfo)
- [Channel Keys](#channel-keys)
- [Data Models](#data-models)
- [Error Handling](#error-handling)
- [Code Examples](#code-examples)

---

## Overview

The Stegasoo REST API provides programmatic access to all steganography operations:

- **Generate** credentials (passphrase, PINs, RSA keys)
- **Encode** messages or files into images (LSB or DCT mode)
- **Decode** messages or files from images (auto-detects mode)
- **Channel keys** for deployment/group isolation (v4.0.0)
- **Analyze** image capacity and compare modes

The API supports both JSON (base64-encoded images) and multipart form data (direct file uploads).

---

## What's New in v4.0.0

Version 4.0.0 adds **channel key** support for deployment/group isolation:

| Feature | Description |
|---------|-------------|
| Channel keys | 256-bit keys that isolate message groups |
| New endpoints | `/channel/status`, `/channel/generate`, `/channel/set`, `DELETE /channel` |
| Encode/decode param | `channel_key` parameter on all encode/decode endpoints |
| Response headers | `X-Stegasoo-Channel-Mode` and `X-Stegasoo-Channel-Fingerprint` |

**Key benefits:**
- ✅ Isolate messages between teams, deployments, or groups
- ✅ Same credentials can't decode messages from different channels
- ✅ Backward compatible (public mode = no channel key)

**Breaking change:** v4.0.0 messages (with channel key) cannot be decoded by v3.x installations.

---

## Installation

### From PyPI

```bash
pip install stegasoo[api]
```

### Running the Server

**Development:**
```bash
cd frontends/api
python main.py
```

**Production:**
```bash
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
```

**Docker with channel key:**
```bash
STEGASOO_CHANNEL_KEY=XXXX-XXXX-... docker-compose up api
```

---

## Base URL

| Environment | URL |
|-------------|-----|
| Local Development | `http://localhost:8000` |
| Docker | `http://localhost:8000` |
| Production | Configure as needed |

---

## Endpoints

### GET / (Status)

Check API status and configuration.

#### Response

```json
{
  "version": "4.0.2",
  "has_argon2": true,
  "has_qrcode_read": true,
  "has_dct": true,
  "max_payload_kb": 500,
  "available_modes": ["lsb", "dct"],
  "dct_features": {
    "output_formats": ["png", "jpeg"],
    "color_modes": ["grayscale", "color"]
  },
  "channel": {
    "mode": "private",
    "configured": true,
    "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456",
    "source": "~/.stegasoo/channel.key"
  },
  "breaking_changes": {
    "v4_channel_key": "Messages encoded with channel key require same key to decode",
    "format_version": 5,
    "backward_compatible": false
  }
}
```

---

### GET /modes

Get available embedding modes and channel status.

#### Response

```json
{
  "lsb": {
    "available": true,
    "name": "Spatial LSB",
    "description": "Embed in pixel LSBs, outputs PNG/BMP",
    "output_format": "PNG (color)",
    "capacity_ratio": "100%"
  },
  "dct": {
    "available": true,
    "name": "DCT Domain",
    "output_formats": ["png", "jpeg"],
    "color_modes": ["grayscale", "color"],
    "capacity_ratio": "~20% of LSB",
    "requires": "scipy"
  },
  "channel": {
    "mode": "private",
    "configured": true,
    "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456"
  }
}
```

---

### GET /channel/status

Get current channel key status. **New in v4.0.0.**

#### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `reveal` | boolean | `false` | Include full key in response |

#### Response

```json
{
  "mode": "private",
  "configured": true,
  "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456",
  "source": "~/.stegasoo/channel.key",
  "key": null
}
```

With `reveal=true`:

```json
{
  "mode": "private",
  "configured": true,
  "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456",
  "source": "~/.stegasoo/channel.key",
  "key": "ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456"
}
```

#### cURL Example

```bash
# Show status
curl http://localhost:8000/channel/status

# Reveal full key
curl "http://localhost:8000/channel/status?reveal=true"
```

---

### POST /channel/generate

Generate a new channel key. **New in v4.0.0.**

#### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `save` | boolean | `false` | Save to user config |
| `save_project` | boolean | `false` | Save to project config |

#### Response

```json
{
  "key": "ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456",
  "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456",
  "saved": true,
  "save_location": "~/.stegasoo/channel.key"
}
```

#### cURL Examples

```bash
# Just generate (don't save)
curl -X POST http://localhost:8000/channel/generate

# Generate and save to user config
curl -X POST "http://localhost:8000/channel/generate?save=true"

# Generate and save to project config
curl -X POST "http://localhost:8000/channel/generate?save_project=true"
```

---

### POST /channel/set

Set/save a channel key to config. **New in v4.0.0.**

#### Request Body

```json
{
  "key": "ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456",
  "location": "user"
}
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `key` | string | required | Channel key |
| `location` | string | `"user"` | `"user"` or `"project"` |

#### Response

```json
{
  "success": true,
  "location": "~/.stegasoo/channel.key",
  "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456"
}
```

---

### DELETE /channel

Clear channel key from config. **New in v4.0.0.**

#### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `location` | string | `"user"` | `"user"`, `"project"`, or `"all"` |

#### Response

```json
{
  "success": true,
  "mode": "public",
  "still_configured": false,
  "remaining_source": null
}
```

#### cURL Example

```bash
# Clear user config
curl -X DELETE http://localhost:8000/channel

# Clear project config
curl -X DELETE "http://localhost:8000/channel?location=project"

# Clear all
curl -X DELETE "http://localhost:8000/channel?location=all"
```

---

### POST /generate

Generate credentials for encoding/decoding.

#### Request Body

```json
{
  "use_pin": true,
  "use_rsa": false,
  "pin_length": 6,
  "rsa_bits": 2048,
  "words_per_passphrase": 4
}
```

#### Response

```json
{
  "passphrase": "abandon ability able about",
  "pin": "847293",
  "rsa_key_pem": null,
  "entropy": {
    "passphrase": 44,
    "pin": 19,
    "rsa": 0,
    "total": 63
  }
}
```

---

### POST /encode (JSON)

Encode a text message into an image.

#### Request Body

```json
{
  "message": "Secret message here",
  "reference_photo_base64": "iVBORw0KGgo...",
  "carrier_image_base64": "iVBORw0KGgo...",
  "passphrase": "apple forest thunder mountain",
  "pin": "123456",
  "rsa_key_base64": null,
  "rsa_password": null,
  "channel_key": null,
  "embed_mode": "lsb",
  "dct_output_format": "png",
  "dct_color_mode": "grayscale"
}
```

#### Channel Key Parameter (v4.0.0)

| Value | Effect |
|-------|--------|
| `null` | Auto mode - use server-configured key |
| `""` (empty string) | Public mode - no channel isolation |
| `"XXXX-XXXX-..."` | Explicit key - use this specific key |

#### Response

```json
{
  "stego_image_base64": "iVBORw0KGgo...",
  "filename": "a1b2c3d4.png",
  "capacity_used_percent": 12.4,
  "embed_mode": "lsb",
  "output_format": "png",
  "color_mode": "color",
  "channel_mode": "private",
  "channel_fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456"
}
```

---

### POST /encode/file

Encode a file into an image (JSON with base64).

Same parameters as `/encode`, plus:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `file_data_base64` | string | ✓ | Base64-encoded file data |
| `filename` | string | ✓ | Original filename |
| `mime_type` | string | | MIME type |

---

### POST /encode/multipart

Encode using multipart form data (file uploads).

#### Form Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `passphrase` | string | ✓ | Passphrase |
| `reference_photo` | file | ✓ | Reference photo |
| `carrier` | file | ✓ | Carrier image |
| `message` | string | * | Text message |
| `payload_file` | file | * | Binary file to embed |
| `pin` | string | | Static PIN |
| `rsa_key` | file | | RSA key (.pem) |
| `rsa_key_qr` | file | | RSA key (QR code image) |
| `rsa_password` | string | | RSA key password |
| `channel_key` | string | | `"auto"` (default), `"none"=public`, or explicit key |
| `embed_mode` | string | | `"lsb"` or `"dct"` |
| `dct_output_format` | string | | `"png"` or `"jpeg"` |
| `dct_color_mode` | string | | `"grayscale"` or `"color"` |

\* Provide either `message` or `payload_file`

#### Channel Key in Multipart

For form data, the channel_key field uses strings:

| Value | Effect |
|-------|--------|
| `"auto"` | Use server config (default) |
| `"none"` | Public mode |
| `"XXXX-XXXX-..."` | Explicit key |

#### Response

Returns the stego image directly with headers:

```http
HTTP/1.1 200 OK
Content-Type: image/png
Content-Disposition: attachment; filename=a1b2c3d4.png
X-Stegasoo-Capacity-Percent: 12.4
X-Stegasoo-Embed-Mode: lsb
X-Stegasoo-Channel-Mode: private
X-Stegasoo-Channel-Fingerprint: ABCD-••••-...-3456
X-Stegasoo-Version: 4.0.2

<binary image data>
```

#### cURL Examples

```bash
# Encode with auto channel key (default)
curl -X POST http://localhost:8000/encode/multipart \
  -F "passphrase=apple forest thunder mountain" \
  -F "pin=123456" \
  -F "message=Secret message" \
  -F "reference_photo=@reference.jpg" \
  -F "carrier=@carrier.png" \
  --output stego.png

# Encode with explicit channel key
curl -X POST http://localhost:8000/encode/multipart \
  -F "passphrase=words here" \
  -F "pin=123456" \
  -F "message=Team message" \
  -F "channel_key=ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456" \
  -F "reference_photo=@reference.jpg" \
  -F "carrier=@carrier.png" \
  --output stego.png

# Encode in public mode (no channel isolation)
curl -X POST http://localhost:8000/encode/multipart \
  -F "passphrase=words here" \
  -F "pin=123456" \
  -F "message=Public message" \
  -F "channel_key=none" \
  -F "reference_photo=@reference.jpg" \
  -F "carrier=@carrier.png" \
  --output stego.png
```

---

### POST /decode (JSON)

Decode a message or file from a stego image.

#### Request Body

```json
{
  "stego_image_base64": "iVBORw0KGgo...",
  "reference_photo_base64": "iVBORw0KGgo...",
  "passphrase": "apple forest thunder mountain",
  "pin": "123456",
  "rsa_key_base64": null,
  "rsa_password": null,
  "channel_key": null,
  "embed_mode": "auto"
}
```

#### Response (Text)

```json
{
  "payload_type": "text",
  "message": "Secret message here",
  "file_data_base64": null,
  "filename": null,
  "mime_type": null
}
```

#### Response (File)

```json
{
  "payload_type": "file",
  "message": null,
  "file_data_base64": "UEsDBBQAAAA...",
  "filename": "document.pdf",
  "mime_type": "application/pdf"
}
```

---

### POST /decode/multipart

Decode using multipart form data.

#### Form Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `passphrase` | string | ✓ | Passphrase |
| `reference_photo` | file | ✓ | Reference photo |
| `stego_image` | file | ✓ | Stego image to decode |
| `pin` | string | | Static PIN |
| `rsa_key` | file | | RSA key (.pem) |
| `rsa_key_qr` | file | | RSA key (QR code image) |
| `rsa_password` | string | | RSA key password |
| `channel_key` | string | | `"auto"` (default), `"none"=public`, or explicit key |
| `embed_mode` | string | | `"auto"`, `"lsb"`, or `"dct"` |

---

## Channel Keys

### Overview

Channel keys provide **deployment/group isolation**. Messages encoded with a channel key can only be decoded with the same key.

### Key Format

```
ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456
└──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘
  8 groups of 4 alphanumeric characters (256 bits)
```

### Storage Locations

Keys are checked in order:

| Priority | Location | Best For |
|----------|----------|----------|
| 1 | `STEGASOO_CHANNEL_KEY` env var | Docker, CI/CD |
| 2 | `./config/channel.key` | Project-specific |
| 3 | `~/.stegasoo/channel.key` | User default |

### API Parameter Values

#### JSON Endpoints (`/encode`, `/decode`)

| Value | Effect |
|-------|--------|
| `null` | Auto - use server config |
| `""` | Public mode |
| `"XXXX-..."` | Explicit key |

#### Multipart Endpoints (`/encode/multipart`, `/decode/multipart`)

| Value | Effect |
|-------|--------|
| `"auto"` | Use server config (default) |
| `"none"` | Public mode |
| `"XXXX-..."` | Explicit key |

### Workflow Example

```bash
# 1. Generate a channel key for the team
KEY=$(curl -s -X POST http://localhost:8000/channel/generate | jq -r '.key')
echo "Team key: $KEY"

# 2. Distribute to team members (securely!)

# 3. Each deployment sets the key
export STEGASOO_CHANNEL_KEY=$KEY

# 4. Encode - automatically uses server key
curl -X POST http://localhost:8000/encode/multipart \
  -F "passphrase=team passphrase" \
  -F "pin=123456" \
  -F "message=Team secret" \
  -F "reference_photo=@ref.jpg" \
  -F "carrier=@carrier.png" \
  --output stego.png

# 5. Decode - automatically uses server key
curl -X POST http://localhost:8000/decode/multipart \
  -F "passphrase=team passphrase" \
  -F "pin=123456" \
  -F "reference_photo=@ref.jpg" \
  -F "stego_image=@stego.png"
```

---

## Data Models

### ChannelStatusResponse

```json
{
  "mode": "private",
  "configured": true,
  "fingerprint": "ABCD-••••-...-3456",
  "source": "~/.stegasoo/channel.key",
  "key": "ABCD-1234-..." 
}
```

### EncodeResponse (v4.0.0)

```json
{
  "stego_image_base64": "string",
  "filename": "string",
  "capacity_used_percent": 12.4,
  "embed_mode": "lsb",
  "output_format": "png",
  "color_mode": "color",
  "channel_mode": "private",
  "channel_fingerprint": "ABCD-••••-...-3456"
}
```

### DecodeResponse

```json
{
  "payload_type": "text",
  "message": "string",
  "file_data_base64": null,
  "filename": null,
  "mime_type": null
}
```

---

## Error Handling

### HTTP Status Codes

| Code | Meaning | Use Case |
|------|---------|----------|
| 200 | OK | Successful operation |
| 400 | Bad Request | Invalid input, capacity error, invalid channel key |
| 401 | Unauthorized | Decryption failed, channel key mismatch |
| 500 | Internal Error | Unexpected server error |
| 501 | Not Implemented | Feature unavailable |

### Channel Key Errors

| Status | Error | Cause |
|--------|-------|-------|
| 400 | "Invalid channel key format" | Key doesn't match `XXXX-XXXX-...` pattern |
| 401 | "Message encoded with channel key but none configured" | Need to provide channel key |
| 401 | "Message encoded without channel key" | Use `channel_key=""` or `"none"` |

---

## Code Examples

### Python

```python
import requests

BASE_URL = "http://localhost:8000"

# Check channel status
status = requests.get(f"{BASE_URL}/channel/status").json()
print(f"Channel mode: {status['mode']}")
print(f"Fingerprint: {status.get('fingerprint', 'N/A')}")

# Generate channel key
response = requests.post(f"{BASE_URL}/channel/generate?save=true")
key_info = response.json()
print(f"Generated: {key_info['fingerprint']}")

# Encode with channel key (auto from server)
with open("ref.jpg", "rb") as ref, open("carrier.png", "rb") as carrier:
    response = requests.post(f"{BASE_URL}/encode/multipart", files={
        "reference_photo": ref,
        "carrier": carrier,
    }, data={
        "message": "Team secret",
        "passphrase": "apple forest thunder",
        "pin": "123456",
        # channel_key defaults to "auto" (use server config)
    })
    
    with open("stego.png", "wb") as f:
        f.write(response.content)
    
    print(f"Channel mode: {response.headers.get('X-Stegasoo-Channel-Mode')}")

# Encode with explicit channel key
with open("ref.jpg", "rb") as ref, open("carrier.png", "rb") as carrier:
    response = requests.post(f"{BASE_URL}/encode/multipart", files={
        "reference_photo": ref,
        "carrier": carrier,
    }, data={
        "message": "Using explicit key",
        "passphrase": "words here",
        "pin": "123456",
        "channel_key": "ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456",
    })

# Decode
with open("ref.jpg", "rb") as ref, open("stego.png", "rb") as stego:
    response = requests.post(f"{BASE_URL}/decode/multipart", files={
        "reference_photo": ref,
        "stego_image": stego,
    }, data={
        "passphrase": "apple forest thunder",
        "pin": "123456",
        # channel_key defaults to "auto"
    })
    
    result = response.json()
    print(f"Decoded: {result.get('message')}")
```

### JavaScript

```javascript
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');

const BASE_URL = 'http://localhost:8000';

async function main() {
    // Check channel status
    const status = await axios.get(`${BASE_URL}/channel/status`);
    console.log('Channel:', status.data.mode);
    
    // Encode with auto channel key
    const form = new FormData();
    form.append('passphrase', 'apple forest thunder');
    form.append('pin', '123456');
    form.append('message', 'Secret');
    form.append('reference_photo', fs.createReadStream('ref.jpg'));
    form.append('carrier', fs.createReadStream('carrier.png'));
    // channel_key defaults to "auto" (use server config)
    
    const response = await axios.post(`${BASE_URL}/encode/multipart`, form, {
        headers: form.getHeaders(),
        responseType: 'arraybuffer'
    });
    
    fs.writeFileSync('stego.png', response.data);
    console.log('Channel mode:', response.headers['x-stegasoo-channel-mode']);
}

main();
```

### cURL / Bash

```bash
#!/bin/bash

BASE_URL="http://localhost:8000"

# Check channel status
echo "Channel status:"
curl -s "$BASE_URL/channel/status" | jq .

# Generate and save channel key
echo "Generating channel key..."
curl -s -X POST "$BASE_URL/channel/generate?save=true" | jq .

# Encode (channel_key defaults to "auto")
echo "Encoding..."
curl -s -X POST "$BASE_URL/encode/multipart" \
  -F "passphrase=apple forest thunder" \
  -F "pin=123456" \
  -F "message=Secret message" \
  -F "reference_photo=@ref.jpg" \
  -F "carrier=@carrier.png" \
  --output stego.png

echo "Encoded to stego.png"

# Decode
echo "Decoding..."
curl -s -X POST "$BASE_URL/decode/multipart" \
  -F "passphrase=apple forest thunder" \
  -F "pin=123456" \
  -F "reference_photo=@ref.jpg" \
  -F "stego_image=@stego.png" | jq .
```

---

## Docker Configuration

### docker-compose.yml

```yaml
x-common-env: &common-env
  STEGASOO_CHANNEL_KEY: ${STEGASOO_CHANNEL_KEY:-}

services:
  api:
    build:
      context: .
      target: api
    ports:
      - "8000:8000"
    environment:
      <<: *common-env
```

### .env (gitignored)

```bash
STEGASOO_CHANNEL_KEY=ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456
```

### Generate key for .env

```bash
curl -s -X POST http://localhost:8000/channel/generate | \
  jq -r '"STEGASOO_CHANNEL_KEY=\(.key)"' >> .env
```

---

## See Also

- [CLI Documentation](CLI.md) - Command-line interface
- [Web UI Documentation](WEB_UI.md) - Browser interface
- [README](../README.md) - Project overview
