Skip to content

feat(vault): browser vault — save/restore session profile via /api/vault/sessions - #23

Merged
iWedmak merged 10 commits into
masterfrom
feature/vault
Oct 8, 2026
Merged

iWedmak merged 10 commits into
masterfrom
feature/vault

Conversation

@iWedmak

@iWedmak iWedmak commented Oct 2, 2026

Copy link
Copy Markdown
Member

Vault 6: Python SDK — работа с vault

Адаптация Python SDK под vault (спека Vault 1-5, бэкенд /api/vault/sessions).

Что сделано

Новый модуль ceki_sdk/_vault.py:

  • ClientVault — HTTP CRUD: list(), get(id) (decrypted profile), create(data, label), update(id, data), delete(id).
  • BrowserVault — два сахарных слоя на живом браузере:
    • browser.vault.save(label=...) — profile.export() → нормализация в per-origin envelope → POST/PUT в vault (PUT, если сессия привязана к rent).
    • browser.vault.restore(id | envelope) — GET vault → session.configure(profile=...): куки сразу, localStorage/sessionStorage буферизуются расширением и флашатся при первой навигации до origin; fingerprint уходит отдельным полем configure.
  • sanitize_cookies — вычёркивает CDP-only поля (priority/size/sourcePort/...), которые браузер не принимает в Network.setCookies.
  • normalize_profile_for_vault / minimal_vault_profile — конвертация плоского profile.export() в envelope и обратно к extension-контракту.

Интеграция:

  • Client.rent(..., vault=id | envelope) — restore профиля прямо при аренде (до fingerprint/masking); браузер биндится на vault-сессию (_vault_session_id).
  • ceki rent --vault SESSION_ID (CLI, один shot + daemon path).
  • README секция «Browser Vault» + пример examples/vault_roundtrip.py.

Тесты: tests/test_vault.py — 18 unit-тестов (mocked httpx + mocked relay WS). Live-раунд-трип против dev-API (list/get/create/update/delete) проверен вручную.

Примечания

  • Vault-эндпоинты под Sanctum-guard и резолвятся на user-токен; SDK шлёт Authorization: Bearer <api_key> — для vault-операций нужен user-token.
  • Python-SDK не имеет ветки dev — MR создан в master (каноничный target для этого репо, как и все прошлые релизы).
  • Поле fingerprint из vault не кладётся внутрь profile configure — применяется отдельным fingerprint-полем (extension обрабатывает его своим путём, чтобы не было двойного применения).

ceki-plugin added 2 commits September 4, 2026 16:55
…ult/sessions

- ceki_sdk/_vault.py: ClientVault HTTP CRUD (list/get/create/update/delete),
  BrowserVault.save (profile.export → vault envelope) and restore
  (session.configure profile+fingerprint), cookie sanitization to the
  settable CDP subset, flat-profile → per-origin envelope normalization.
- Client.rent(vault=...) restores the vault profile before applying
  fingerprint/masking; the browser is bound to the vault session id so a
  later browser.vault.save() overwrites it (PUT).
- Browser.vault surface wired in __init__; CLI rent --vault SESSION_ID
  (one-shot + daemon path) and README vault section + example script.
- tests/test_vault.py: 18 unit tests (mocked httpx + mocked relay); live
  dev-API roundtrip verified separately.
@iWedmak iWedmak self-assigned this Oct 2, 2026
ceki-plugin and others added 8 commits October 2, 2026 12:23
Task 11622 (Vault 7). Adds the `ceki vault` subcommand surface on top of
ClientVault/BrowserVault from Vault 6 (SDK):

- vault list [--json] [--per-page N]
- vault get ID [--json] [-o FILE]  (decrypted profile)
- vault save FILE [--id ID] [--label L]
- vault save --session SID [--id ID] [--no-session-storage]  (live snapshot)
- vault apply ID --session SID | --schedule N
- vault delete ID

Vault commands run over plain HTTP (no relay session), using the same
Client/api_url + basic-auth overrides as connect(). Also fixes `ceki rent
--vault` not forwarding the vault id through daemon IPC.

Tests: tests/test_cli_vault.py (parser + mocked handler coverage).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Cosmetic only — no logic change.
- unused imports (AsyncMock/MagicMock/os/PropertyMock) removed
- import block sorted (I001)
- two over-long lines wrapped (E501)
- duplicate test_dispatch_webrtc_answer_no_ice_servers definition dropped
  (identical duplicate; kept the fuller variant with wait_dc_open)

No logic change; 163 tests still pass.
… W292)

- _browser.py: wrap 3 over-long log.debug lines
- _webrtc.py: drop redundant aiortc.RTCIceCandidate import in add_ice_candidate
  (_parse_ice_candidate imports it itself), sort imports, wrap long type hints
- examples/vault_roundtrip.py: trailing newline

No behavior change; ruff check . clean, vault+p2p tests green.
ceki rent without CEKI_API_KEY used to reach the daemon path and exit
with a confusing code=daemon (6) when a daemon was running. Require the
key up front so the CLI exits cleanly with code=auth (2), matching the
test expectation and the one-shot fallback behavior.

Fixes test_missing_api_key_exits_2 (41 cli tests pass).
… preserve -1011 reason

Two real defects surfaced by the mock-relay tests (which measure actual
SDK behavior):

1. Client.rent() waited for _p2p_ready for up to 15s even when no P2P
   signaling had started (_p2p is None — e.g. relay/extension that never
   answers a webrtc.offer). Every rent paid a 15s stall on the WS path,
   growing to ~45s per rent/cdp test. Only wait when P2P was actually
   initiated (self._p2p is not None).

2. Relay error -1011 (heartbeat_timeout) / -1018 was routed through
   browser._on_session_ended(), which set reason="completed" when the
   message had no "reason" field — clobbering the real terminal reason
   and never calling _on_error (which knows heartbeat_timeout). Route
   -1011/-1018 through _on_error (correct reason + exception), then run
   the session.ended cleanup hook. _on_session_ended also now refuses to
   overwrite an already-set precise reason with a generic "completed".

Tests: test_rent_flow 7/7, test_browser_cdp 4/4, test_multi_session 2/2,
test_error_1011_heartbeat_timeout pass; vault 40/40 still green.
…p-race tests

- Browser.send(): if the P2P DataChannel doesn't open within a short grace
  (3s instead of 30s), fall back to WS for the whole session (set
  _p2p_fallback) — previously every subsequent send() re-paid the 30s
  wait_dc_open() stall, and send(timeout=5) tests always timed out.
- tests/test_browser_errors.py: wait up to 5s (was 0.05s/1s) for the
  client's cdp WS message before asserting — the fixed 0.05s sleep was a
  race under CI load (cdp not yet written to relay.received).
- tests/test_daemon.py: -1011 now routes through _on_error (preserves
  reason) then the cleanup hook; updated the test to assert that.

All previously-failing CI tests green locally: rent_flow 7/7,
browser_cdp 4/4, browser_errors 4/4, multi_session 2/2, daemon 1011 ok;
vault 40/40 still pass.
@iWedmak
iWedmak merged commit 4fd79d2 into master Oct 8, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant