Skip to content

Latest commit

 

History

History
428 lines (302 loc) · 9.94 KB

File metadata and controls

428 lines (302 loc) · 9.94 KB

REST API reference

Base URL: http://<host>:<port> (default http://0.0.0.0:8765).

Authentication

When api.api_key is configured, all write endpoints (POST, PUT, DELETE, PATCH) require:

X-API-Key: <value>

Read endpoints (GET) are unauthenticated (_check_api_key, api.py:111).

Missing or incorrect key returns HTTP 401.


Dashboard and system

GET /

Returns the HTML dashboard. Served by Jinja2; not suitable for API clients.

GET /health

Returns 200 if all enabled mounts are healthy (or mounting/disabled). Returns 503 if any enabled mount is in error, unmounted, or stale (health_check, api.py:242).

200 response:

{"ok": true}

503 response:

{"ok": false, "unhealthy": ["nas", "backup"]}

GET /api/version

Returns daemon version string (api_version, api.py:190).

{"version": "0.1.0"}

GET /metrics

Prometheus exposition format (text/plain; version=0.0.4) (prometheus_metrics, api.py:196).

Metrics (all with {name="<mount_name>"} labels):

Metric Type Description
sshfs_keeper_mount_healthy gauge 1 if healthy, 0 otherwise
sshfs_keeper_mount_count counter Total successful mounts since start
sshfs_keeper_mount_retry_count gauge Current consecutive retry count
sshfs_keeper_mount_duration_seconds gauge Last mount operation duration
sshfs_keeper_sync_run_count counter Total sync job runs since start
sshfs_keeper_sync_fail_count gauge Consecutive failure count (resets on success)
sshfs_keeper_sync_bytes_sent gauge Bytes sent in last successful run
sshfs_keeper_sync_last_duration_seconds gauge Duration of last sync run

Generated by metrics.generate (metrics.py:24).

GET /api/events

Server-Sent Events stream. Each event is a JSON object on the data: line (sse_events, api.py:204).

The stream emits two SSE events per status change:

  • event: <event_type>: for example mount_healthy, mount_error
  • event: mount_update_<name>: per-mount event for HTMX out-of-band swap

A : keepalive comment is sent every 30 seconds when there is no activity.

Event payload:

{
  "event": "mount_healthy",
  "mount": "nas",
  "status": "healthy",
  "timestamp": 1711234567.123
}

Mount status

GET /api/status

Returns snapshot of all mount states (api_status, api.py:235).

{
  "mounts": [
    {
      "name": "nas",
      "remote": "user@host:/path",
      "local": "/mnt/nas",
      "enabled": true,
      "mount_tool": "sshfs",
      "status": "healthy",
      "last_check": 1711234567.0,
      "last_mounted": 1711234500.0,
      "last_error": null,
      "retry_count": 0,
      "mount_count": 3,
      "backoff_remaining": 0.0,
      "mount_duration_seconds": 1.23,
      "usage": {
        "total_gb": 500.0,
        "used_gb": 200.0,
        "free_gb": 300.0,
        "percent_used": 40.0
      }
    }
  ],
  "timestamp": 1711234567.123
}

status values: healthy, unmounted, stale, mounting, disabled, error (MountStatus, monitor.py:16).

usage is populated only when status is healthy; otherwise null (Monitor.get_snapshot, monitor.py:102).


Mount CRUD

POST /api/mounts

Create a new mount. Returns 409 if name already exists (api_add_mount, api.py:346).

Request body:

Field Type Required Default
name string yes none
remote string yes none
local string yes none
options string no "cache=yes,compression=yes,ServerAliveInterval=15,ServerAliveCountMax=3,reconnect"
identity string | null no null
enabled bool no true
mount_tool string no "sshfs"

Response:

{"name": "nas", "created": true}

PUT /api/mounts/{name}

Update an existing mount. Rename is supported by setting a different name in the body; returns 409 if the new name conflicts (api_update_mount, api.py:370).

Same body fields as POST /api/mounts.

Response:

{"name": "nas", "updated": true}

DELETE /api/mounts/{name}

Remove a mount config from memory and disk. Does not unmount (api_delete_mount, api.py:402).

Response:

{"name": "nas", "deleted": true}

POST /api/mounts/{name}/remount

Resets backoff and retry counter, then triggers an immediate remount attempt (api_remount, api.py:418).

Response:

{"name": "nas", "success": true}

POST /api/mounts/{name}/unmount

Force-unmounts using fusermount3 -uz (Linux) or umount -f (macOS) (api_unmount, api.py:430).

Response:

{"name": "nas", "success": true}

POST /api/mounts/{name}/enable

Sets enabled = true and saves config (api_enable, api.py:443).

Response:

{"name": "nas", "enabled": true}

POST /api/mounts/{name}/disable

Sets enabled = false and saves config (api_disable, api.py:455).

Response:

{"name": "nas", "enabled": false}

PATCH /api/mounts/{name}/backend

Toggles mount_tool between sshfs and rclone (api_switch_backend, api.py:467).

Response:

{"name": "nas", "mount_tool": "rclone"}

Daemon settings

PUT /api/settings

Update daemon settings in-memory and persist to disk (api_update_settings, api.py:494).

Request body (all fields optional; only provided fields are updated):

Field Type
check_interval int
remount_delay int
max_retries int
backoff_base int
log_level string
json_logs bool

Response:

{"updated": true}

Notification settings

GET /api/notifications

Returns current notification settings (api_get_notifications, api.py:516).

{
  "webhook_url": "https://ntfy.sh/my-topic",
  "on_failure": true,
  "on_recovery": true,
  "on_backoff": false
}

PUT /api/notifications

Update notification settings and save config (api_update_notifications, api.py:528).

Request body:

Field Type Default
webhook_url string | null null
on_failure bool true
on_recovery bool true
on_backoff bool false

Response:

{"updated": true}

SSH key management

Keys are stored in ~/.config/sshfs-keeper/keys/ at mode 600 (api_upload_key, api.py:556).

GET /api/keys

List stored private key filenames (api_list_keys, api.py:551).

{"keys": ["id_ed25519", "id_rsa"]}

POST /api/keys

Upload a private key file (multipart/form-data, field name file). File must begin with PRIVATE KEY or OPENSSH (api_upload_key, api.py:556).

Response:

{"name": "id_ed25519", "path": "/home/user/.config/sshfs-keeper/keys/id_ed25519"}

DELETE /api/keys/{name}

Delete a stored key (api_delete_key, api.py:580).

Response:

{"name": "id_ed25519", "deleted": true}

Sync jobs

GET /api/syncs

Returns snapshot of all sync job states (api_list_syncs, api.py:597).

{
  "syncs": [
    {
      "name": "backup",
      "source": "/data/",
      "target": "user@host:/backup/",
      "interval": 3600,
      "options": "-az --delete --stats",
      "enabled": true,
      "status": "ok",
      "last_run": 1711234000.0,
      "last_duration": 12.3,
      "last_error": null,
      "bytes_sent": 102400,
      "files_transferred": 5,
      "run_count": 10,
      "fail_count": 0,
      "sync_tool": "rsync",
      "next_run_in": 3543.7
    }
  ]
}

status values: idle, running, ok, failed, disabled (SyncStatus, sync.py:22).

POST /api/syncs

Create a sync job (api_add_sync, api.py:601).

Request body:

Field Type Required Default
name string yes none
source string yes none
target string yes none
interval int no 3600
options string no "-az --delete --stats"
identity string | null no null
enabled bool no true
sync_tool string no "rsync"

Response:

{"name": "backup", "created": true}

PUT /api/syncs/{name}

Update a sync job. Rename supported (api_update_sync, api.py:620).

Same body as POST /api/syncs.

Response:

{"name": "backup", "updated": true}

DELETE /api/syncs/{name}

Remove a sync job (api_delete_sync, api.py:649).

Response:

{"name": "backup", "deleted": true}

POST /api/syncs/{name}/trigger

Run the sync job immediately (sets _next_run = 0) (api_trigger_sync, api.py:663).

Response:

{"name": "backup", "triggered": true}

GET /api/syncs/{name}/log

Returns the last 50 lines of stdout+stderr from the most recent sync run (api_sync_log, api.py:673).

When called with HX-Request header, returns plain HTML text. Otherwise returns JSON:

{"name": "backup", "lines": ["sent 1024 bytes ...", "..."]}

POST /api/syncs/{name}/enable / POST /api/syncs/{name}/disable

Toggle enabled state and save config (api_enable_sync, api.py:695), api_disable_sync (api.py:707).

{"name": "backup", "enabled": true}

HTMX fragment endpoints

These endpoints return HTML partials for in-place dashboard refresh. They are consumed by the dashboard and not intended for API clients.

Path Handler Template
GET /fragments/mounts fragment_mounts (api.py:263) _mount_cards.html
GET /fragments/syncs fragment_syncs (api.py:277) _sync_cards.html
GET /fragments/mounts/{name} fragment_mount_card (api.py:288) _mount_card.html
GET /fragments/syncs/{name} fragment_sync_card (api.py:309) _sync_card.html
GET /fragments/keys fragment_keys (api.py:331) _keys_list.html

← Configuration · Home · Architecture →