Metadata-Version: 2.4
Name: alter-runtime
Version: 0.3.1
Summary: ~Alter Identity Runtime - local sovereign daemon for the continuous identity field. Subscribes to per-~handle Cloudflare Durable Objects, exposes Unix socket + D-Bus + CLI surfaces, falls back gracefully to direct MCP polling.
Project-URL: Homepage, https://truealter.com
Project-URL: Documentation, https://truealter.com/docs/alter-runtime
Project-URL: Repository, https://github.com/true-alter/alter-runtime
Project-URL: Bug Tracker, https://github.com/true-alter/alter-runtime/issues
Author: Blake Morrison
Author-email: ALTER Meridian Pty Ltd <hello@truealter.com>
License: Apache-2.0
License-File: LICENSE
Keywords: agent,ai-agents,alter,cloudflare,durable-object,identity,llm,mcp,model-context-protocol,psychometric,sovereign-identity,verifiable-credentials,x402
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Distributed Computing
Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
Requires-Python: >=3.10
Requires-Dist: cryptography>=42.0
Requires-Dist: httpx-sse>=0.4.0
Requires-Dist: httpx>=0.26.0
Requires-Dist: pathspec>=0.12.0
Requires-Dist: pydantic>=2.5.0
Requires-Dist: pyyaml>=6.0.1
Requires-Dist: tree-sitter-python>=0.23.0
Requires-Dist: tree-sitter>=0.23.0
Requires-Dist: watchdog>=4.0.0
Provides-Extra: all
Requires-Dist: bcc>=0.29.0; (sys_platform == 'linux') and extra == 'all'
Requires-Dist: dbus-next>=0.2.3; (sys_platform == 'linux') and extra == 'all'
Requires-Dist: pywin32>=306; (sys_platform == 'win32') and extra == 'all'
Requires-Dist: slack-bolt>=1.18.0; extra == 'all'
Requires-Dist: slack-sdk>=3.27.0; extra == 'all'
Requires-Dist: systemd-python>=235; (sys_platform == 'linux') and extra == 'all'
Provides-Extra: dbus
Requires-Dist: dbus-next>=0.2.3; (sys_platform == 'linux') and extra == 'dbus'
Provides-Extra: dev
Requires-Dist: jsonschema>=4.21.0; extra == 'dev'
Requires-Dist: mypy>=1.10.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.5.0; extra == 'dev'
Provides-Extra: ebpf
Requires-Dist: bcc>=0.29.0; (sys_platform == 'linux') and extra == 'ebpf'
Provides-Extra: slack
Requires-Dist: slack-bolt>=1.18.0; extra == 'slack'
Requires-Dist: slack-sdk>=3.27.0; extra == 'slack'
Provides-Extra: systemd
Requires-Dist: systemd-python>=235; (sys_platform == 'linux') and extra == 'systemd'
Provides-Extra: windows
Requires-Dist: pywin32>=306; (sys_platform == 'win32') and extra == 'windows'
Description-Content-Type: text/markdown

# alter-runtime - ~Alter Identity Runtime

> **L3 of the six-layer identity distribution surface.** The local sovereign daemon that
> lives alongside you, subscribes to your per-`~handle` Cloudflare Durable Object, and
> renders your identity field across every surface your device touches - terminal, IDE,
> status bars, desktop tray, browser extension. Falls back gracefully to direct MCP
> polling when the edge is unreachable.

The alter-runtime daemon architecture was established in April 2026.

## Alpha release - filesystem key storage

**`0.1.0` is an alpha release.** The public API surface (CLI flags,
Unix-socket JSON schema, D-Bus interface, Python SDK signatures) may change in
backwards-incompatible ways before the `0.2.0` stable cut. Pin exact versions in
downstream tooling and read the CHANGELOG before upgrading.

**Private keys are stored on the filesystem, not in an OS keychain.** On first
`alter-runtime init`, the device Ed25519 keypair is written to
`~/.config/alter/keypair.json` (or `$XDG_CONFIG_HOME/alter/keypair.json` if the
XDG variable is set) with `0600` permissions. The file is readable only by the
owning user and never leaves the device. This behaviour is deliberate for the
alpha: it keeps the daemon self-contained on any POSIX host and avoids a hard
dependency on distro-specific secret stores during early testing.

**OS-native secret storage is deferred to `0.2.0`:** Linux `libsecret` /
`kwallet`, macOS Keychain, and Windows Credential Manager integration will ship
behind a `keystore = "native"` config knob. Filesystem storage will remain the
documented fallback.

**For high-stakes use during the alpha** - any deployment where a device
compromise would imply identity compromise - pair the runtime with
hardware-attested passkey registration via the browser claim flow (graduated
attestation). The filesystem-stored device key then scopes only to ambient
signal ingestion, not to Sovereign-tier authorisations, which require a fresh
hardware passkey ceremony per session.

**Reporting issues.** Security-relevant reports: please follow
[SECURITY.md](./SECURITY.md) and email `security@truealter.com` rather than
filing a public issue. Non-security bugs and feature requests: email
`support@truealter.com`.

## Status

| Wave | Stream | Status |
|------|--------|--------|
| 1 | 1c - Daemon skeleton + `AlterClient` SDK | Shipped |
| 2 | 2b - Subscribers, Unix socket, D-Bus, git watcher | Shipped |
| 2 | 2c - systemd + launchd service units | Shipped |
| 2 | 2d - First pixel: CC hook + scripts upgrade | Shipped |
| 2 | 2e - eBPF subscriber (Patent M, reference impl in `alter-ebpf`) | Shipped |
| 3 | 3a - Cross-platform tray surfaces | Planned |
| 3 | 3b - Windows Service + `pam_alter` stub | Planned |
| 3 | 3c - PyPI release CI + signed binaries | Planned |

## Install

```bash
# PyPI (cross-platform)
pip install truealter

# Arch Linux (AUR)
pacman -S alter-runtime          # via your AUR helper (yay -S alter-runtime, paru -S alter-runtime, etc.)

# macOS / Linux Homebrew tap
brew install alter-runtime

# Optional extras (advanced - direct install of the runtime package)
pip install 'alter-runtime[dbus,systemd]'          # Linux desktop
pip install 'alter-runtime[windows]'                # Windows
pip install 'alter-runtime[all]'                    # Everything
```

## Quickstart

```bash
# 1. Generate device keypair, install host service unit, authenticate via alter-cli
alter-runtime init

# 2. Start the daemon
alter-runtime start            # Launches via systemd/launchd/Windows Service
# or run in the foreground for debugging
alter-runtime daemon

# 3. Query current field state
alter-runtime status
alter-runtime query attunement

# 4. Manually ingest a signal (useful for testing)
alter-runtime ingest --kind git_commit --payload '{"sha":"abc123"}'

# 5. Stop
alter-runtime stop
```

## What it does

1. **Subscribes** to your per-`~handle` Cloudflare Durable Object over Server-Sent Events
   at `https://mcp.truealter.com/events/~yourhandle/stream`. Events arrive with ~50ms
   latency worldwide.

2. **Falls back gracefully** to direct polling of the ALTER MCP endpoint at
   `https://api.truealter.com/api/v1/mcp` when the DO is unreachable for more than 3
   seconds. Your surfaces never know which path served them.

3. **Exposes three local transports:**
   - **Unix socket** at `/run/user/$UID/alter.sock` (Linux) or
     `~/Library/Application Support/alter/runtime.sock` (macOS) - line-delimited JSON
   - **D-Bus** interface `org.alter.Identity1` on the session bus (Linux) - used by
     GNOME/KDE/Waybar modules
   - **HTTP/SSE** loopback at `http://127.0.0.1:<port>/events` - used by the CC hook
     and shell scripts

4. **Collects ambient signals** via adapters (Wave 2):
   - Git commits, branch switches, pushes (via `watchdog` on `.git/refs/heads/`)
   - CC hook events (forwarded from `.claude/hooks/*.sh`)
   - Shell command invocations (opt-in)
   - eBPF kernel attestations (shipped; reference impl in `alter-ebpf`, Patent M)

5. **Maintains a local cache** of your last-known-good field state so the first-paint
   tilde warmth renders in <1 second, even offline. Per IFA's Five OS-Native Properties,
   this is the **monotonic continuity** guarantee.

## Layout

```
alter-runtime/
├── pyproject.toml
├── README.md
├── LICENSE
├── alter_runtime/
│   ├── __init__.py
│   ├── config.py                 # XDG loader, reads alter-cli session.json
│   ├── daemon.py                 # asyncio supervisor
│   ├── cli.py                    # argparse entrypoint: init|start|stop|status|query|ingest|daemon
│   ├── sdk/
│   │   ├── __init__.py
│   │   └── client.py             # AlterClient (lifted from backend/openclaw-skill)
│   ├── subscribers/              # (Wave 2)
│   │   ├── do_sse.py             # primary - subscribes to handle-alter DO SSE
│   │   └── mcp_fallback.py       # fallback - polls api.truealter.com/api/v1/mcp
│   ├── sockets/                  # (Wave 2)
│   │   ├── unix.py               # /run/user/$UID/alter.sock
│   │   └── dbus.py               # org.alter.Identity1
│   ├── adapters/                 # (Wave 2)
│   │   └── git_watcher.py        # watchdog on .git/refs/heads/
│   └── services/                 # (Wave 2)
│       ├── systemd/alter-runtime.service
│       ├── launchd/com.alter.runtime.plist
│       └── windows/AlterRuntimeService.py
└── tests/
    ├── conftest.py
    ├── test_cli.py
    ├── test_daemon.py
    └── test_client.py
```

## SDK usage

```python
import asyncio
from alter_runtime import AlterClient

async def main():
    # Auto-discovers the local daemon's Unix socket first, falls back to
    # direct MCP if the daemon isn't running.
    client = AlterClient.auto_discover()

    async with client:
        whoami = await client.whoami()
        print(f"{whoami['handle']}: attunement={whoami['attunement']}")

        # Ingest an ambient signal
        await client.ingest(
            kind="tool_invocation",
            payload={"tool": "my-custom-cli", "duration_ms": 42},
        )

asyncio.run(main())
```

## Packaging

- **PyPI** - `pip install alter-runtime` (publish is tag-gated; see
  `.github/workflows/ci.yml`).
- **AUR** - draft PKGBUILD at [`aur/PKGBUILD`](aur/PKGBUILD). Submission is
  blocked on the PyPI publish resolving the sdist URL. Regenerate `.SRCINFO`
  with `makepkg --printsrcinfo > .SRCINFO` before AUR upload.
- **`truealter` PyPI shim** - thin console-script package at
  [`pypi-truealter/`](pypi-truealter/) that execs the npm-installed
  `alter` binary. Distinct from `alter-runtime`: `pip install truealter`
  gets pip-centric users a fourth CLI install channel alongside npm,
  Homebrew (planned), and the AUR (planned). Tag format
  `truealter-v<version>`; see
  [`.github/workflows/truealter-publish.yml`](.github/workflows/truealter-publish.yml).

## Contributing

After cloning, wire the identity-trailer hook so commits land with `Acted-By:`, `Drafted-With:`, and `Co-Authored-By:` trailers automatically:

```
bash scripts/setup-githooks.sh
```

The helper sets the local `core.hooksPath` to `.githooks/` and marks every hook executable. It is idempotent - safe to re-run after pulls. Equivalent one-liner without the helper:

```
git config core.hooksPath .githooks
```

The hook reads your ALTER session at `~/.config/alter/session.json` (run `alter login` from the [`@truealter/cli`](https://www.npmjs.com/package/@truealter/cli) once) plus `$CLAUDE_MODEL` for the Instrument tier attribution. It is silent when the session is missing or `jq` is unavailable, so a contributor without an ALTER session still gets a normal commit; the CI verifier catches missing trailers at PR time (warn-only during the Rung 1/2 migration window).

Python projects don't have npm's `prepare`-on-install lifecycle, so this step is manual - done once after clone and persisted in your local `.git/config`.

## Related projects

- [`@truealter/sdk`](https://www.npmjs.com/package/@truealter/sdk) - TypeScript
  SDK client for the public MCP server.
- ALTER MCP endpoint - `https://mcp.truealter.com` (SSE per-`~handle` stream)
  and `https://api.truealter.com/api/v1/mcp` (HTTP fallback). Both are documented
  at [truealter.com](https://truealter.com).
