The Airgapper API provides remote control of backup and restore operations.
airgapper serve # Default port :8081, or set AIRGAPPER_PORTGET /healthReturns server health status.
Response:
{
"success": true,
"data": {
"status": "ok"
}
}GET /api/statusReturns current system status.
Response:
{
"success": true,
"data": {
"name": "alice",
"role": "owner",
"repo_url": "rest:http://localhost:8000/backup",
"has_share": true,
"share_index": 1,
"pending_requests": 0,
"peer": {
"name": "bob",
"address": "http://bob:8081"
}
}
}GET /api/requestsReturns all pending restore requests.
Response:
{
"success": true,
"data": [
{
"id": "a1b2c3d4",
"requester": "alice",
"snapshot_id": "latest",
"paths": null,
"reason": "laptop crashed",
"status": "pending",
"created_at": "2024-01-25T10:00:00Z",
"expires_at": "2024-01-26T10:00:00Z"
}
]
}POST /api/requests
Content-Type: application/json
{
"snapshot_id": "latest",
"paths": ["/home/user/documents"],
"reason": "need to restore files"
}Creates a new restore request.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
snapshot_id |
string | No | Snapshot to restore (default: "latest") |
paths |
string[] | No | Specific paths to restore |
reason |
string | Yes | Reason for restore request |
Response:
{
"success": true,
"data": {
"id": "a1b2c3d4",
"status": "pending",
"expires_at": "2024-01-26T10:00:00Z"
}
}GET /api/requests/{id}Returns details of a specific request.
Response:
{
"success": true,
"data": {
"id": "a1b2c3d4",
"requester": "alice",
"snapshot_id": "latest",
"paths": null,
"reason": "laptop crashed",
"status": "approved",
"created_at": "2024-01-25T10:00:00Z",
"expires_at": "2024-01-26T10:00:00Z",
"approved_at": "2024-01-25T11:00:00Z",
"approved_by": "bob"
}
}POST /api/requests/{id}/approve
Content-Type: application/json
{}Approves a restore request and releases the local key share.
Optional Body:
{
"share": "base64-encoded-share",
"share_index": 2
}If no body provided, uses the locally stored share.
Response:
{
"success": true,
"data": {
"status": "approved",
"message": "Key share released"
}
}POST /api/requests/{id}/denyDenies a restore request.
Response:
{
"success": true,
"data": {
"status": "denied"
}
}GET /api/snapshotsLists available snapshots (requires password/authorization).
Response:
{
"success": true,
"data": {
"message": "Snapshot listing requires restore approval"
}
}Note: In the current implementation, snapshot listing requires the full password, which is only available to the owner.
POST /api/share
Content-Type: application/json
{
"share": "base64-encoded-share",
"share_index": 2,
"repo_url": "rest:http://localhost:8000/backup",
"peer_name": "alice"
}Receives and stores a key share from a peer. Used during initial setup.
Response:
{
"success": true,
"data": {
"status": "received",
"message": "Share stored successfully"
}
}All errors return a consistent format:
{
"success": false,
"error": "Error message description"
}Common HTTP Status Codes:
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad request (invalid input) |
| 404 | Not found |
| 500 | Internal server error |
# Health check
curl http://localhost:8081/health
# Get status
curl http://localhost:8081/api/status
# Create restore request
curl -X POST http://localhost:8081/api/requests \
-H "Content-Type: application/json" \
-d '{"reason": "need files back", "snapshot_id": "latest"}'
# List pending requests
curl http://localhost:8081/api/requests
# Approve a request
curl -X POST http://localhost:8081/api/requests/a1b2c3d4/approve
# Deny a request
curl -X POST http://localhost:8081/api/requests/a1b2c3d4/deny# Health check
http :8081/health
# Get status
http :8081/api/status
# Create request
http POST :8081/api/requests reason="need restore" snapshot_id="latest"
# Approve
http POST :8081/api/requests/a1b2c3d4/approveimport requests
BASE_URL = "http://localhost:8081"
# Get status
resp = requests.get(f"{BASE_URL}/api/status")
print(resp.json())
# Create request
resp = requests.post(f"{BASE_URL}/api/requests", json={
"reason": "laptop died",
"snapshot_id": "latest"
})
request_id = resp.json()["data"]["id"]
# Approve (on Bob's side)
resp = requests.post(f"{BASE_URL}/api/requests/{request_id}/approve")
print(resp.json())The API includes CORS headers allowing cross-origin requests:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
-
No built-in authentication - The current API has no auth. In production:
- Use a reverse proxy with authentication
- Add API keys
- Use mTLS
-
TLS recommended - Use HTTPS in production
-
Network isolation - Consider running on a private network
-
Audit logging - All API calls are logged to stdout
Future versions may include WebSocket support for:
- Real-time request notifications
- Backup progress updates
- Peer status monitoring