Skip to main content

framework-shells

framework-shells is the Python implementation of FWS, a small process-runtime layer for apps that need to run child processes and keep them observable.

Use it when your application starts long-lived workers or tools and you want one consistent way to:

  • launch them
  • stop them cleanly
  • group them by app/project/purpose
  • capture stdout/stderr logs
  • write to stdin when the backend supports it
  • inspect recent output
  • expose a dashboard or API for runtime control
  • share runtime state with other FWS-compatible managers

Examples of things FWS can supervise:

  • app workers
  • language servers
  • build workers
  • file-system or git helpers
  • JSON-RPC stdio services
  • terminal brokers
  • adapter processes
  • any other child process that should not be a hidden subprocess.Popen(...)

framework-shells is not the application protocol. It does not know what your JSON-RPC methods mean, how your DTOs are shaped, or how your business logic routes requests. It owns process lifecycle and observability. Your application owns protocol semantics.

For Rust-native hosts or paths where Python should not own process I/O, use ferrous-framework, the Rust implementation of the same FWS runtime contract.

Mental Model

An FWS runtime is a process manager plus a small metadata store.

When FWS starts a child process, it creates a shell record. A shell record answers questions like:

  • what command was launched?
  • what backend owns it?
  • what PID is currently running?
  • where are stdout/stderr logs?
  • what labels and groups does it belong to?
  • can this manager write to stdin?
  • can this manager stream output live?
  • is the process still running or has it exited?

Those records live under a runtime-specific directory. The runtime is keyed by a repo fingerprint and secret, so different projects or manager roots do not accidentally claim each other's processes.

~/.cache/framework_shells/runtimes/<repo_fingerprint>/<runtime_id>/
  meta/<shell_id>/meta.json
  logs/<shell_id>.stdout.log
  logs/<shell_id>.stderr.log

Backends

FWS intentionally keeps the backend model small.

Backend Use it for Shape
proc app workers, services, build jobs, helpers that do not need stdin supervised process plus stdout/stderr logs
pipe JSON-RPC stdio servers, protocol adapters, language workers, structured backend tools supervised stdin/stdout/stderr byte streams
pty interactive shells, terminal applications, TUI-like processes supervised terminal byte stream with input and resize

The pipe backend is protocol-neutral. It does not parse JSON-RPC, line protocols, editor control messages, or application DTOs. It owns the child process, stdin writes, stdout/stderr capture, logs, capabilities, and shutdown. The consumer owns framing and semantics.

PTY-backed shells support pty_mode="raw" for byte-oriented compatibility and pty_mode="interactive" for normal cooked/echoing terminal behavior.

Shellspecs

A shellspec is a YAML launch file.

It is not a new protocol. It is just a declarative way to say: "these are the runtime processes this app may need, and here is how to launch them."

A shellspec gives FWS a stable process contract:

  • shell id inside the spec
  • backend (proc, pipe, or pty)
  • command and arguments
  • working directory
  • environment variables
  • labels and subgroups
  • optional readiness checks
  • optional dashboard/UI hints
  • optional debug/inspection flags

Minimal example:

version: "1"
shells:
  api:
    backend: proc
    cwd: ${ctx:PROJECT_ROOT}
    command: ["python", "-m", "http.server", "${free_port}"]
    env:
      PORT: ${free_port}
      APP_ID: ${ctx:APP_ID}

In that example:

  • api is the shellspec entry id.
  • ${ctx:PROJECT_ROOT} and ${ctx:APP_ID} come from the render context supplied by the host app.
  • ${free_port} asks FWS to reserve and reuse one free port during rendering.
  • FWS launches the rendered command and stores the resulting shell record.

Starting a shellspec entry from Python:

from framework_shells import get_manager
from framework_shells.orchestrator import Orchestrator

mgr = await get_manager()
shell = await Orchestrator(mgr).start_from_ref(
    "shellspec/app_worker.yaml#api",
    base_dir=app_dir,
    ctx={"APP_ID": "demo", "PROJECT_ROOT": str(project_root)},
    label="app-worker:demo",
)

A pipe shellspec for a stdio JSON-RPC worker:

version: "1"
shells:
  rpc_worker:
    backend: pipe
    cwd: ${ctx:PROJECT_ROOT}
    command: ["python", "-m", "my_app.rpc_worker"]
    subgroups: ["demo", "rpc"]
    inspect_hints:
      - json
      - jsonrpc

FWS will supervise the process and preserve stdout/stderr observability. Your app still owns the JSON-RPC reader, writer, request routing, and DTO validation.

Quick Python Usage

from framework_shells import get_manager

mgr = await get_manager()

# A normal supervised process.
proc = await mgr.spawn_shell(
    ["python", "-m", "http.server", "8080"],
    label="demo-server",
)

# A stdio worker with live stdin/stdout while this manager owns it.
pipe = await mgr.spawn_shell_pipe(
    ["python", "-m", "my_app.rpc_worker"],
    label="rpc-worker",
)
await mgr.write_to_shell(pipe.id, '{"jsonrpc":"2.0","id":1,"method":"ping"}', append_newline=True)

# An interactive terminal process.
pty = await mgr.spawn_shell_pty(
    ["bash", "-l", "-i"],
    label="terminal",
    pty_mode="interactive",
)
await mgr.resize_pty(pty.id, cols=120, rows=40)

# Inspect and control.
shells = await mgr.list_shells()
tail = await mgr.get_log_tail(pipe.id, stream="stdout", lines=100)
await mgr.terminate_shell(proc.id, force=False)

CLI

The fws CLI uses the same runtime store and secret resolution as the Python manager.

# List current runtime shells.
fws list
fws list --all

# Start all autostart entries from a shellspec file.
fws up shells.yaml

# Start a one-off process.
fws run --backend proc --label demo -- python -m http.server 8080

# Inspect recent output.
fws inspect <shell_id>
fws inspect <shell_id> --stream stdout --format jsonrpc --json

# Write to live stdin when the owning manager exposes input.
fws write <shell_id> '{"jsonrpc":"2.0","id":"probe","method":"ping","params":{}}' --newline
cat payload.json | fws write <shell_id> - --json --newline

# Stop or remove shells.
fws terminate <shell_id>
fws rm <shell_id>
fws shutdown-group <app_id>
fws down --tree

# Show process trees.
fws tree --depth 4

Dashboard And API

framework-shells can be mounted into a FastAPI/ASGI host. The mounted runtime exposes:

  • REST endpoints for shell list/detail/control
  • live dashboard updates
  • live log streaming
  • shell stdin injection for capable live shells
  • log inspection helpers
  • runtime auth derived from FRAMEWORK_SHELLS_SECRET

The helper mount is:

from framework_shells.api.socketio_backend import mount_fws_dashboard_runtime

mount_fws_dashboard_runtime(app)

Important routes:

GET    /fws/                                      dashboard
GET    /api/framework_shells                     list shells
GET    /api/framework_shells/{id}                shell detail
POST   /api/framework_shells/{id}/terminate      terminate shell
POST   /api/framework_shells/{id}/input          write stdin / send EOF when supported
DELETE /api/framework_shells/{id}                remove metadata and logs
GET    /api/framework_shells/logs/{id}/tail      structured log tail
GET    /api/framework_shells/logs/{id}/inspect   plain/json/jsonrpc inspection
POST   /api/framework_shells/app/{app_id}/shutdown  group shutdown

The dashboard is an observability/control surface. It is not required for the supervised process to run.

Logs And Inspection

Raw stdout and stderr logs stay raw. FWS does not rewrite process output to add timestamps or protocol metadata.

Inspection is a read-side helper over the raw logs. It can classify recent output as:

  • plain
  • json
  • jsonrpc

For deeper debugging, a shell can opt into I/O metadata sidecars:

version: "1"
shells:
  rpc_worker:
    backend: pipe
    command: ["python", "-m", "my_app.rpc_worker"]
    debug:
      io_metadata: true

When enabled, FWS writes a sibling JSONL sidecar with output chunk metadata and stdin write records. Stdin is never appended to raw stdout/stderr logs.

CLI example:

fws inspect <shell_id> --io-metadata --stdin --timestamps --json

Bounded Log Views And Binary Protocols

The dashboard keeps a sliding window of up to 200 source records. Scrolling near an edge fetches another slice while retaining overlap and your reading position. Scrolling away detaches live following; Jump to live restores the newest window. Existing events signal new output without polling or accumulating hidden history. Tap a pane header to expand or collapse STDOUT, STDERR, or available STDIN input. STDOUT opens first; per-shell collapse and divider sizes persist locally. A shared Wrap checkbox controls stream wrapping (on by default). Records have subtle separators and no individual height cap; oversized records still show explicit projection omissions. Raw files are unchanged, and original-byte retrieval remains available through the raw API in pages of at most 64 KiB, not as dashboard hex controls.

Declare how each stream should be observed in its shellspec:

log_codecs:
  stdout: messagepack
  stderr: text

Supported values are text (default), json, and messagepack. Templates such as ${env:STDOUT_CODEC} work too. MessagePack means concatenated complete objects, not newline-separated binary. It changes observation, not stdin/stdout bytes or Socket.IO serialization. fws inspect automatically decodes declared MessagePack logs, including Ferrous records in the shared runtime.

Window/raw APIs live at /api/framework_shells/logs/{shell_id}/window and /raw. See the projection plan for budgets and current limits, including display-window filtering and historical ANSI state.

Runtime Boundaries

FWS owns:

  • process launch
  • shutdown and group shutdown
  • process metadata
  • stdout/stderr log paths
  • live input/output capability reporting
  • dashboard/API/control surfaces
  • peer-manager coordination across Python and Ferrous runtimes

Your application owns:

  • protocol framing
  • request/response correlation
  • DTOs
  • schema validation
  • business logic
  • user-facing app behavior

This boundary is the reason pipe is a byte stream instead of a JSON-RPC framework. FWS should make protocol workers observable and controllable without becoming part of their protocol.

Native Packaging

Git source installs default to auto native packaging.

If Cargo is available, setup attempts to build and bundle:

  • the native terminal broker binary
  • the native pipe reader extension

If native build fails in auto mode, the install continues with the pure-Python package. To require native artifacts:

FRAMEWORK_SHELLS_INSTALL_MODE=build \
FRAMEWORK_SHELLS_PIPE_PUMP_MODE=build \
python -m pip install "framework-shells @ git+https://github.com/mrsurge/framework-shells@main"

To force Python-only install:

FRAMEWORK_SHELLS_INSTALL_MODE=python-only \
python -m pip install "framework-shells @ git+https://github.com/mrsurge/framework-shells@main"

Free-threaded Termux Python is supported by the source build path. The PyO3 pipe pump is built as a version-specific extension such as:

fws_pipe_pump.cpython-314t-aarch64-linux-android.so

Environment Variables

Variable Description
FRAMEWORK_SHELLS_SECRET Secret for runtime ID derivation and API auth
FRAMEWORK_SHELLS_REPO_FINGERPRINT Override auto-computed repo fingerprint
FRAMEWORK_SHELLS_BASE_DIR Override storage base dir, default ~/.cache/framework_shells
FRAMEWORK_SHELLS_PTY_MODE Default PTY mode for new PTY shells, raw or interactive
FRAMEWORK_SHELLS_INSTALL_MODE Native packaging mode, auto, build, or python-only
FRAMEWORK_SHELLS_PIPE_PUMP_MODE Native pipe extension packaging mode, defaults to install mode

If FRAMEWORK_SHELLS_SECRET is missing, the CLI tries to load or create the runtime secret under the current repo fingerprint. If the secret is set, mutating API endpoints require a derived token via X-Framework-Key or Authorization: Bearer ....

Directory Layout

framework_shells/
  manager.py              core FrameworkShellManager
  record.py               ShellRecord metadata model
  store.py                runtime-scoped storage paths
  shellspec.py            YAML shellspec loader and renderer
  orchestrator.py         shellspec start/apply helpers
  events.py               process-local lifecycle/output event bus
  shutdown.py             shutdown planner/executor helpers
  native_pipe.py          optional native pipe extension loader
  terminal_stream_broker.py  Python terminal-stream fallback broker
  api/                    REST, dashboard, and Socket.IO runtime
  cli/                    fws command
  protocols/              typed JSON-RPC/dashboard/peer contracts
  ui/                     dashboard frontend assets

Screenshots

The dashboard can run as a standalone page or be embedded by a host application.

Dashboard Logs
dash.png logs.png

Metadata

Release files for framework-shells 0.0.64

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for framework-shells 0.0.64
File Size Uploaded
framework_shells-0.0.64.tar.gz 847.7 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for framework-shells 0.0.64
File Interpreter ABI Platform
framework_shells-0.0.64-cp314-cp314t-manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 free-threading Linux glibc 2.28+ x86-64 Details
framework_shells-0.0.64-cp39-abi3-manylinux_2_28_x86_64.whl CPython 3.9 abi3 Linux glibc 2.28+ x86-64 Details
framework_shells-0.0.64-cp39-abi3-android_24_arm64_v8a.whl CPython 3.9 abi3 Android API level 24+ ARM64 v8a Details

Total release size: 3.4 MB

Release files / framework_shells-0.0.64.tar.gz

Download URL framework_shells-0.0.64.tar.gz
Size 847.7 kB
Tags Source
SHA-256 checksum
How to use checksums
a92e6d0ddaced7c9b19d7d94ac5cc37b2d3e9b1befc109a0f4f93f256ef1413c
BLAKE2b-256 checksum
How to use checksums
492f014e59fc0e4994918e2571de2f12018c85f768b79a0ef960d9bdc1664074
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / framework_shells-0.0.64-cp314-cp314t-manylinux_2_28_x86_64.whl

Download URL framework_shells-0.0.64-cp314-cp314t-manylinux_2_28_x86_64.whl
Size 834.1 kB
Tags CPython 3.14 CPython 3.14 free-threading Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
3f028a8e5080592a925acafaed60259abb27b9727a0f7330cfddfd156dc21e83
BLAKE2b-256 checksum
How to use checksums
6892c39761a7da73233d345a76364462baef433dab50c7f665bd4c597c31d174
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / framework_shells-0.0.64-cp39-abi3-manylinux_2_28_x86_64.whl

Download URL framework_shells-0.0.64-cp39-abi3-manylinux_2_28_x86_64.whl
Size 842.1 kB
Tags CPython 3.9 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
324966f857fccf1e8656a1b6e76ce74fe665ca0fa07517ee2352ede36de76cf4
BLAKE2b-256 checksum
How to use checksums
3737c12273ce5db3e730bd52ce605571bc7a9d39091e42342bc876e6c9db959b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / framework_shells-0.0.64-cp39-abi3-android_24_arm64_v8a.whl

Download URL framework_shells-0.0.64-cp39-abi3-android_24_arm64_v8a.whl
Size 873.2 kB
Tags Android API level 24+ ARM64 v8a CPython 3.9 abi3
SHA-256 checksum
How to use checksums
8e42a191e723f594352b1ff698b9672db813fa5bf796d42222aca54bdf1bdbea
BLAKE2b-256 checksum
How to use checksums
f5d1cfb905de2c1b9bf3b902518e6a24238edd35b1d8ffda42556dbed15c127f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

0.0.64 This release

4 release files

0.0.63

4 release 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