LimeBat account and encrypted sync API v1

Base URL: https://limebat.com/api/v1. This standalone API is owned by website/limebat (Manage-Tasks/limebatweb.git). It does not use or proxy through ERP/Gateway. SSH traffic still goes directly from the apps to the user's servers.

Send Accept: application/json; requests with a body use Content-Type: application/json. Authenticated routes require Authorization: Bearer TOKEN. HTTPS is mandatory in production. Responses are private/non-cacheable. No route accepts an SSH password, private key, or vault recovery key in plaintext.

Accounts and devices

Method/path Request Response / permission
POST /auth/login email (valid, ≤254 chars), password (nonempty, ≤72 chars), device_name (nonempty, ≤120 chars) 200 with token, ISO expires_at, and user; creates a device token valid for 30 days
GET /me — 200 {"user": {"id":"UUID","name":"Name","email":"[email protected]","sync_enabled":true}}; authenticated active account
POST /auth/logout — 204; revokes the current device token
GET /devices — 200 {"devices":[{"id":"123","name":"LimeBat · ios","current":true,"last_used_at":null,"expires_at":"ISO timestamp"}]}; only this account's unexpired tokens
DELETE /devices/{id} Numeric token ID 204; revoke only an owned device; another account's ID returns 404
GET /vault — 200 {"revision":0,"envelope":null} initially; authenticated active account with sync_enabled=true
PUT /vault expected_revision and envelope, below 200 {"revision":N+1} only if current revision equals N; same sync entitlement required

IDs in user responses are opaque UUIDs, not internal database IDs. Token values are returned only at login; the database stores Sanctum's token hashes. There can be at most 20 unexpired device tokens per account. Revoke an old device before adding more. Disabling an account blocks every token. Resetting its password using the administrator command revokes all device tokens and retains encrypted data. There is no public signup, billing or password-recovery endpoint in this phase. An administrator provisions accounts and sync entitlements with limebat:user. Applications must never grant their own entitlement or treat a local flag as server authorization. Disabling sync returns 403 for both vault routes; it does not delete the vault, local profiles or access to account/device management.

Login is limited to 5 attempts/minute per normalized email and 10/minute per IP. Authenticated API routes are limited to 120 requests/minute per account. Generic validation failures use 422 {"message":"…","errors":{"field":["…"]}}. Login failures do not distinguish an unknown, disabled, or wrong-password account. Missing, expired, revoked or disabled-account tokens return 401. No sync entitlement returns 403; missing/foreign devices return 404. Rate/device limits return 429. Retry after the indicated rate limit or revoke a device as appropriate. Requests larger than 1,500,000 bytes return 413 (a proxy may reject them earlier). Production plaintext HTTP receives 400 rather than a redirect that could forward credentials. Temporary service failures return 5xx.

Encrypted vault format and concurrency

Example PUT request shape (base64 placeholders must be replaced by valid bytes):

{
  "expected_revision": 0,
  "envelope": {
    "algorithm": "AES-256-GCM",
    "nonce": "BASE64_OF_12_RANDOM_BYTES",
    "ciphertext": "BASE64_OF_ENCRYPTED_VAULT",
    "tag": "BASE64_OF_16_AUTHENTICATION_BYTES"
  }
}

expected_revision is an integer 0–1,000,000,000. Envelope keys are exactly the four above. Encoding is canonical padded standard base64. Ciphertext decodes to 1–1,048,576 bytes; the nonce is 12 bytes and tag 16 bytes. The server validates the envelope structure and size, but cannot decrypt or inspect its contents. It stores one account-owned snapshot, never shares it between users, and atomically compares and increments the revision. Stale writes return 409 {"message":"…","code":"revision_conflict"} without changing stored data. Clients must fetch, decrypt and merge before retrying. A lost success response is reconciled by fetching the current snapshot rather than blindly overwriting it.

The apps generate a random 256-bit recovery key, represented as unpadded URL-safe base64 (43 characters). It is not derived from the account password. AES-256-GCM uses a fresh random nonce per encryption and the UTF-8 additional authenticated data limebat:v1:ACCOUNT_UUID:NEW_REVISION. The decrypted JSON is:

{
  "version": 1,
  "hosts": [{
    "id": "CLIENT_GENERATED_ID", "name": "Example", "address": "192.0.2.10",
    "username": "alice", "port": 22, "group": "Work", "authMethod": "password",
    "password": "", "privateKey": "", "passphrase": ""
  }],
  "pins": {"[192.0.2.10]:22": "ssh-ed25519 SHA256:VERIFIED_FINGERPRINT"}
}

This plaintext example describes the client contract; it is never the API request body. IDs are unique per snapshot, ports are 1–65535, and there are at most 10,000 profiles within the encrypted byte limit. authMethod is password or privateKey. SSH passwords, private keys and passphrases remain inside the ciphertext. Terminal output and running sessions are never synchronized.

Clients save a local base snapshot and revision atomically with their working vault in platform secure storage. Independent changes merge automatically by profile ID; deletion versus edit and simultaneous conflicting edits require explicit review. Any differing locally trusted host fingerprint also requires review, even when the local profile was otherwise unchanged. A remote deletion never silently clears an existing local trusted identity. Explicit user choices apply only to the reviewed cloud revision. Lower-than-known cloud revisions, wrong keys, modified ciphertext and unsupported formats stop sync without replacing the local vault. This protocol does not hide account metadata or payload sizes and cannot detect a malicious server replaying history to an entirely new device without a previously known revision.

Recovery, logout, revocation and offline operation

Sign-in is required initially. First sync setup displays a recovery key and requires acknowledgment that it was saved. A new device signs in, supplies this key, and downloads/decrypts the configurations. Existing signed-in devices retain an account-scoped secure copy and can work offline; edits sync on foreground resume, after edits, or during a 30-second foreground poll. There is no promise of background execution on iOS/Android. A revoked/expired token locks the app at its next authenticated response. Reauthentication to the same account preserves unsynced local work. A different account never inherits that work or its key.

Explicit logout closes SSH sessions and deletes that account's local vault and recovery key. Unsynced work must either sync first or be explicitly discarded. If offline logout cannot revoke the server token, it still clears local access and tells the user to revoke the device elsewhere. Remote revocation cannot erase already-downloaded data from an offline device. Losing every recovery-key copy means encrypted data is unrecoverable, even after an account password reset. There is no silent recovery-key reset or destructive vault reset route. Back up the recovery key separately; administrator account recovery cannot decrypt it.

Existing pre-account profiles stay in their original secure device vault until the user explicitly imports them into the signed-in account. That import is recorded once and retains the original as a local backup. Payment collection, self-service registration, account deletion/export, key rotation and a complete lost-device recovery UI remain release work; do not sell subscriptions yet.