Skip to main content

skharness

Sovereign "phone-drives-my-agent-swarm" harness.

Run many coding/work agents, each in an isolated session, drive them from a phone over the tailnet, and let the autocode engine drive them unattended behind a merge gate. The feel of Claude Code's remote control, self-hosted on SKWorld infra with no Big-Tech broker.

Operational docs: SOP.md. Security posture and reporting: SECURITY.md. Contributing: CONTRIBUTING.md.

Why

Claude Code Remote Control has great UX but routes phone to laptop through Anthropic's relay, which fails the sovereignty test. skharness replicates the experience, not the foundation, over Tailscale + capauth. (Design basis: the 2026-06-13 harness deep-research covering pi, cmux, OpenCode, web-shells, and Claude Code Remote Control. Spec: docs/superpowers/specs/2026-06-13-skharness-design.md. That spec predates the current write surface; where it disagrees with SOP.md, trust the SOP.)

What is in here

Two things ship in one distribution:

  1. skcode-hostd, the per-host daemon: a capauth-gated FastAPI over one Harness (the claude-code tmux adapter), bound to a Tailscale IP on port 9394.
  2. The autocode engine (src/skharness/autocode/): the assess/plan/build/grade/ finalize loop, the twin gate, and the constitutional carve-out detector. skos.autopilot delegates to it.

Architecture

Module Role
serve.py skcode-hostd entry point. resolve_bind() refuses a wildcard bind; select_verifier() picks real capauth or the fail-closed deny-all.
daemon.py Every HTTP/WS route, plus PUBLIC_ROUTES and ROUTE_SCOPES, the authoritative scope map.
auth.py The bearer gate and the AuthContext scope carrier. Fail closed before the verifier runs.
harnesses/claude_code.py The tmux harness and the spawn() guard, including the dispatch repo allowlist.
operator_cli.py The explain / observe / act operator facet Atlas drives.
autocode/orchestrator.py Engine phases 0-3, caps, kill switch.
autocode/engineering.py Worktree, sandbox, grade, twin gate, finalize(). The merge choke point.
autocode/protected.py Path-level carve-out detector. Fails closed: any manifest load failure protects everything.
manager.py / registry.py / session.py / spawner.py The session-core primitives from the P0 design.

Full diagram and entry-point tour: SOP.md section 2.

Target designs: continual harness defines safe refinement and recovery; the Evolution Arena defines controlled multi-agent experiments, Pi/SKGateway execution, independent verification, artifact lineage, and Pareto promotion. Both are explicitly proposed; their documents distinguish implemented evidence from planned architecture.

Run

pip install -e ".[dev]"

# tailnet IP only; a wildcard or blank host is refused by resolve_bind()
~/.skenv/bin/python -m skharness --host <your-tailscale-ip> --port 9394 --host-id .158

Managed deploy (systemd user unit, never auto-started):

./systemd/install.sh          # then provision ~/.config/skcode-hostd/skcode-hostd.env
systemctl --user enable --now skcode-hostd

See SOP.md section 5 for the full deploy, rollback, and the two live drop-ins.

Exposure

Tailnet only. There is no public route. No :443 vhost, no Cloudflare Tunnel, no Funnel. serve.resolve_bind() raises rather than binding 0.0.0.0 or ::, and the unit sources --host from ${SKCODE_HOSTD_TAILSCALE_IP}, so a missing value fails the unit closed instead of exposing a port. Do not add a fallback default.

Port 9394 is the ratified default (SKWorld platform spec R0.4). :9390 belongs to skcomms.transports.broker_server, hence the offset.

Routes and the write surface

This daemon has a real write surface. Earlier revisions of this README claimed it did not; that claim was stale and is corrected here.

Method Path Required scope
GET /.well-known/skworld-module.json, /, /app public (static client and manifest)
GET /api/v1/hosts/self skcode.stream
GET /api/v1/sessions, /api/v1/sessions/{sid}, /api/v1/sessions/{sid}/events skcode.stream
GET /api/v1/jobs, /api/v1/watchdog/digest skcode.stream
WS /api/v1/sessions/{sid}/stream skcode.stream (token rides ?token=)
GET /api/v1/dispatch/targets skcode.dispatch
POST /api/v1/sessions/{sid}/ratify skcode.inject (grades only, never merges)
POST /api/v1/sessions/{sid}/inject skcode.inject (keystrokes into a live PTY)
POST /api/v1/sessions/{sid}/deny skcode.inject
POST /api/v1/dispatch skcode.dispatch (spawns a NEW session: the RCE surface)
POST /api/v1/sessions/{sid}/cancel skcode.dispatch

There is no /health or /healthz route. Use GET /api/v1/hosts/self or systemctl --user is-active skcode-hostd.

Four gates stand in front of the write surface, each failing closed:

  1. Bearer (auth.require_bearer): a missing or empty token is rejected before the verifier runs.
  2. capauth verification (serve.build_capauth_verifier): the wire token is base64url-decoded, import_tokened, and checked with verify_audience_token(t, "skcode"). Any parse or verify failure denies. If capauth cannot be imported, select_verifier() falls back to deny all.
  3. Scope split: skcode.stream reads, skcode.inject writes, skcode.dispatch spawns. A read-only token can view everything and actuate nothing.
  4. capauth PDP: inject and dispatch additionally require a capauth.authz.decide allow at a VERIFIED enrollment floor. Dispatch also needs an audit sink (no sink means 501), honours the pause flag (503), and restricts the full profile to an explicit subject allowlist.

tests/test_route_coverage.py enumerates the live route table and fails if any served route is not classified as public or scope-gated, so a new gated route cannot ship unclassified.

Dispatch allowlist

POST /api/v1/dispatch can spawn a new agent session, which is remote code execution. SKCODE_DISPATCH_REPOS (comma-separated absolute repo roots) is the last gate. Unset or empty means DENY ALL, and the shipped env template deliberately omits the key, so a fresh install can dispatch nothing.

skos and skharness must never be added to it. They are the self-modification hazard: an agent dispatched into either could edit the very code that grades it. The enforcement is the deployed env value alone; there is no code-level exclusion list. See SOP.md section 6.

Crypto posture

skharness generates, stores, and wraps no key material. Its entire cryptographic footprint is verification: one call into capauth's verify_audience_token over an already-issued token, behind the Verifier seam in src/skharness/auth.py. It performs no key exchange, no KEM, no signature generation. No post-quantum claim is made or implied here; the posture of the tokens it verifies belongs to capauth.

The authorization model, the twin gate, and the carve-out detector are operational but not independently audited. The sandbox is isolation for accidents, not a hostile-code boundary: treat agent-authored code as untrusted input to review, not as contained.

Test

python -m pytest tests/ -q

CI (.github/workflows/ci.yml) runs lint, test, compat-3-10, and build. The test job installs the real siblings from git main (skcoord last, with --upgrade) and fails if the cross-repo round-trip test skips, because a skipped gate reports green while checking nothing. Details: SOP.md section 4.

Version and license

The version comes from setuptools-scm, derived from the newest v*.*.* git tag, and a release tag is cut automatically by publish.yml on a push to main. Read it with python -m setuptools_scm in a checkout with tags. History: CHANGELOG.md.

Licensed GPL-3.0-or-later. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

skharness-0.3.34.tar.gz (873.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

skharness-0.3.34-py3-none-any.whl (395.6 kB view details)

Uploaded Python 3

File details

Details for the file skharness-0.3.34.tar.gz.

File metadata

  • Download URL: skharness-0.3.34.tar.gz
  • Upload date:
  • Size: 873.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for skharness-0.3.34.tar.gz
Algorithm Hash digest
SHA256 c64d82ab5e4df46fc1e32c21d88562161ad3a642771f33e610074b143261ac92
MD5 ac3b1c665bb645b69abe475b03fe2ba5
BLAKE2b-256 96651f8e91494daa4c823a7051f4b703840617f2840153a228a95ec8394394ef

See more details on using hashes here.

Provenance

The following attestation bundles were made for skharness-0.3.34.tar.gz:

Publisher: publish.yml on smilinTux/skharness

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file skharness-0.3.34-py3-none-any.whl.

File metadata

  • Download URL: skharness-0.3.34-py3-none-any.whl
  • Upload date:
  • Size: 395.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for skharness-0.3.34-py3-none-any.whl
Algorithm Hash digest
SHA256 45410fac3bfaa8068ad1fcd384a36c4fae3f165714c7e591836bb887c4da8c94
MD5 5391a89aca86041b5321bc9244137ce3
BLAKE2b-256 7d7083cb25002644d16156954c49661b0693364e810eb6afd4584e7afd6b1495

See more details on using hashes here.

Provenance

The following attestation bundles were made for skharness-0.3.34-py3-none-any.whl:

Publisher: publish.yml on smilinTux/skharness

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.44

2 files

0.3.43

2 files

0.3.42

2 files

0.3.41

2 files

0.3.40

2 files

0.3.39

2 files

0.3.38

2 files

0.3.37

2 files

0.3.36

2 files

0.3.35

2 files

This release

0.3.34 This release

2 files

0.3.33

2 files

0.3.32

2 files

0.3.31

2 files

0.3.30

2 files

0.3.29

2 files

0.3.28

2 files

0.3.27

2 files

0.3.26

2 files

0.3.25

2 files

0.3.24

2 files

0.3.23

2 files

0.3.22

2 files

0.3.21

2 files

0.3.20

2 files

0.3.19

2 files

0.3.18

2 files

0.3.17

2 files

0.3.16

2 files

0.3.15

2 files

0.3.14

2 files

0.3.12

2 files

0.3.11

2 files

0.3.10

2 files

0.3.9

2 files

0.3.8

2 files

0.3.7

2 files

0.3.6

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page