# services/

Business-logic services used by routers, agents, and workers.

## runtime_probe.py

`RuntimeProbe` performs bounded, non-blocking availability checks for execution
runtimes (local / docker / remote k8s). Each probe returns a `ProbeResult`
dataclass and never raises — failures are surfaced as `ok=False` with a
human-readable `reason`.

- Docker probe shells `docker info --format json` with a 3s timeout and caches
  the result for 30 seconds via a monotonic clock.
- The k8s remote probe is currently a stub returning
  `"Cloud pairing required"`; it will integrate with the pairing state once
  that lands.
- Access via the process-wide singleton `get_runtime_probe()`.

Used by `app/routers/desktop.py` to power the tray/runtime-probe endpoints.

## cloud_client.py + token_store.py

Desktop sidecar HTTP transport to the cloud companion. `CloudClient` is an
`httpx.AsyncClient` wrapper with bearer injection (sourced lazily from
`token_store.get_cloud_token()`), bounded 5xx retries (0.5s/1s/2s), and a
consecutive-failure circuit breaker (5 failures in 60s → open for 30s).
4xx never retries; `asyncio.CancelledError` propagates. Streaming requests
skip the retry loop but still respect the breaker. Use `get_cloud_client()`
for the process singleton, or instantiate directly in tests with a custom
`transport=httpx.MockTransport(...)` / `respx` router.

`token_store` is the on-disk contract for the eventual Tauri shell:
`$TESSLATE_CLOUD_TOKEN` env var wins, else `cache/cloud_token.json` under
`$OPENSAIL_HOME` (atomic write, 0600 on POSIX). No network I/O.

Both modules are non-blocking on failure: routers should `try/except` and
fall back to cached/empty data so the desktop UI stays responsive when the
cloud is unreachable.

## marketplace_installer.py

Per-source install pipeline for marketplace items (Wave 6). Two entry
points:

- `install_from_source(source, kind, slug, version=None, ...)` — primary.
  Resolves the bundle envelope via the source's `MarketplaceClient`
  (`get_bundle`), enforces `expires_at` / `archive_format` / size cap (the
  strictest of `envelope.size_bytes`, `policies_cache.max_bundle_size_bytes[kind]`,
  per-kind default, and a 1 GB hard ceiling), streams the download with
  bearer-LESS `httpx.AsyncClient` while computing sha256 + tracking bytes,
  optionally verifies an ed25519 attestation, then extracts via
  `install_extract.safe_extract` (the ONLY extraction path — no
  `tarfile.extractall` anywhere). For `source.base_url.startswith('local://')`
  it short-circuits to `marketplace_local.get_bundle_envelope` /
  `materialise_bundle` — no HTTP attempt.
- `install(kind, slug)` — legacy cloud-mediated path; preserved as the
  fallback for the existing `POST /api/desktop/marketplace/install`
  endpoint and for paid-item flows that still route through the cloud.

Every failure raises a typed subclass of `InstallError`
(`BundleSha256MismatchError`, `BundleSizeExceededError`,
`BundleExpiredError`, `BundleFormatError`, `AttestationError`,
`AlreadyInstalledError`, `InvalidKindError`, `InvalidSlugError`,
`CloudFallbackUnavailableError`) — each carries a stable
machine-readable `reason` token so routers / UI can branch without
string-matching the message. Partial installs land in
`{slug}.installing/` and are removed on any error.

`uninstall(kind, slug) -> bool` removes the directory; returns False if
nothing was there.

## install_extract.py

Path-traversal-hardened tar.zst extractor used by
`marketplace_installer.py`. `safe_extract(archive_path, dest_root,
max_uncompressed_bytes=1 GB)` rejects: non-tar.zst archives (extension
AND magic-byte check), absolute paths, parent-traversal segments,
null-byte names, symlinks/hardlinks targeting outside `dest_root`,
unsupported member types (devices, FIFOs), and tar bombs (declared or
actual size > max). Sets safe modes (strips setuid / setgid / world-write).
The marketplace service has its own copy at
`packages/tesslate-marketplace/app/services/install_check.py`; the two
implementations stay in sync but live in separate codebases.

Typed errors: `UnsafeArchiveError` (with `reason` token like
`parent_traversal`, `absolute_path`, `null_byte`, `symlink_escapes_destination`,
`hardlink_escapes_destination`, `unsupported_member_type`),
`UnsupportedArchiveFormatError`, `ArchiveTooLargeError`. Every malicious
archive in the corpus at `tests/security/fixtures/malicious_archives/`
is regenerated by fixture functions on each test run.

## marketplace_local.py

Filesystem-backed sentinel marketplace source for desktop mode (Wave 6).
`scan_kind(kind)` walks `$OPENSAIL_HOME/{kind}s/{slug}/` (or
`{slug}/{version}/`) and emits `LocalItemRecord` rows for every directory
with a `manifest.json`. `get_bundle_envelope(kind, slug, version=None)`
returns a `LocalEnvelope` mirroring the `/v1/.../bundle` shape so the
installer can branch only on `url.startswith('local://')`. Bundles are
either pre-built (`bundle.tar.zst`) or synthesised deterministically from
the directory tree (sorted entries, mtime=0) so sha256 is reproducible.
`sync_local(db)` emits virtual `upsert` / `delete` change events the
caller applies to the catalog cache for the system Local source.
`sync_local_loop(session_factory)` is the desktop's 15-minute periodic
runner registered in `app/main.py`.

## handoff_client.py

Pure serialization layer for agent ticket hand-offs between the desktop
orchestrator and a cloud peer. `push(session, ticket_id=...)` loads an
`AgentTask` and returns a frozen `HandoffBundle` (ticket id, title,
goal ancestry, plus trajectory/diff/skill-binding placeholders).
`pull(session, cloud_task_id=..., bundle=..., project_id=...)` creates a
fresh local ticket preserving ancestry and tagging the cloud origin. No
network I/O — HTTP transport and real trajectory/diff wiring are deferred
to later slices.
