{"id":50799300,"url":"https://github.com/aiterm-io/aiterm-connector","last_synced_at":"2026-06-12T18:00:36.403Z","repository":{"id":352834862,"uuid":"1216839679","full_name":"aiterm-io/aiterm-connector","owner":"aiterm-io","description":"Thin-client connector for the AITerm multi-AI terminal SaaS platform","archived":false,"fork":false,"pushed_at":"2026-06-08T08:51:19.000Z","size":445,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-08T10:25:33.325Z","etag":null,"topics":["ai","claude","llm","ollama","pty","python","saas","self-hosted","terminal","websocket"],"latest_commit_sha":null,"homepage":"https://aiterm.io","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aiterm-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-21T09:30:39.000Z","updated_at":"2026-06-08T08:51:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/aiterm-io/aiterm-connector","commit_stats":null,"previous_names":["aiterm-io/aiterm-connector"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/aiterm-io/aiterm-connector","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiterm-io%2Faiterm-connector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiterm-io%2Faiterm-connector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiterm-io%2Faiterm-connector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiterm-io%2Faiterm-connector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aiterm-io","download_url":"https://codeload.github.com/aiterm-io/aiterm-connector/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiterm-io%2Faiterm-connector/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34256188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","claude","llm","ollama","pty","python","saas","self-hosted","terminal","websocket"],"created_at":"2026-06-12T18:00:21.743Z","updated_at":"2026-06-12T18:00:36.396Z","avatar_url":"https://github.com/aiterm-io.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AITerm Connector\n\n[![CI](https://github.com/aiterm-io/aiterm-connector/actions/workflows/ci.yml/badge.svg)](https://github.com/aiterm-io/aiterm-connector/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/)\n[![GitHub release](https://img.shields.io/github/v/release/aiterm-io/aiterm-connector?include_prereleases\u0026sort=semver)](https://github.com/aiterm-io/aiterm-connector/releases)\n\nLightweight remote agent for [AITerm](https://aiterm.io) — a multi-AI terminal SaaS platform. This connector runs on your machines and relays terminal sessions to the AITerm hub so you can reach your AI backends from a browser.\n\nThe connector is **MIT-licensed and fully auditable**. The hub (backend, dashboard, billing) is closed source.\n\n## What it does\n\n- Discovers local AI backends (Claude Code, Ollama, llama.cpp, LM Studio, vLLM, GPT4All) plus plain bash.\n- Spawns PTY sessions on demand and streams them to the hub.\n- Connects **outbound** to the hub (push-model): works behind NAT, no inbound ports.\n- Self-updates via Ed25519-signed manifest.\n\n    Browser → Hub (wss://aiterm.io) ↔ Connector ↔ PTY Manager → AI process\n\n## Install\n\n    curl -sSL https://aiterm.io/install | bash\n\nThe installer asks whether to install **system-wide** (`/opt/aiterm`, systemd at boot) or **per-user** (`~/.local/share/aiterm`, `systemctl --user`). It prints a pairing URL — open it in your browser to link the machine to your account.\n\n## Requirements\n\n- Python 3.9+\n- `websockets`, `cryptography` (installed automatically)\n- `systemd` (optional — installer falls back to `nohup` otherwise)\n\n## Components\n\n| File               | Role                                                              |\n|--------------------|-------------------------------------------------------------------|\n| `connector.py`     | WebSocket client. Auth, self-update, file uploads, message relay. |\n| `pty-manager.py`   | Multi-session PTY host. Survives connector restarts.              |\n| `aiterm`           | CLI wrapper: `aiterm status / start / stop / update / uninstall`. |\n| `install.sh`       | Installer + updater (dual-mode, hash-verified).                   |\n\nThe connector is a thin client: it relays JSON messages between the hub (WebSocket) and the PTY manager (Unix socket). No business logic lives here.\n\n## Security\n\n- **TLS certificate pinning (TOFU)** — first-seen hub cert is stored in `.cert_pin`; any change aborts with 60s backoff.\n- **Ed25519-signed updates** — `remote_update` fetches `manifest.json` + `manifest.sig`, verifies against the public key embedded in `connector.py` (`MANIFEST_PUBKEY_HEX`). Tampered manifest = rejected.\n- **SHA-256 per-file verification** — each downloaded file's hash must match the signed manifest.\n- **Environment sanitization** — spawned PTY sessions get a whitelisted env (`PATH, HOME, USER, SHELL, TERM, COLORTERM, LANG, LC_*, TZ, DISPLAY, XDG_*`). `AWS_*`, `ANTHROPIC_API_KEY`, `GITHUB_TOKEN`, `*_SECRET` and the like are **not** inherited.\n- **Upload hardening** — `O_NOFOLLOW | O_EXCL` on writes, symlink check on the upload dir.\n- **Systemd hardening** — `NoNewPrivileges`, `ProtectHome=read-only`, `ProtectKernel*`, `RestrictSUIDSGID`, `RestrictNamespaces`, `LockPersonality`.\n- **Allowlisted upload extensions** — images, audio, video, PDF. No SVG (XSS risk).\n- **Configurable hub URL** — `connector.json` can point at any WebSocket endpoint; this repo isn't tied to aiterm.io.\n\n### Reviewing the update path\n\nIf you want to verify that `remote_update` cannot be used for remote code execution without the signing key:\n\n1. `self_update()` in `connector.py` — fetches `manifest.json` + `manifest.sig`, verifies Ed25519 signature against `MANIFEST_PUBKEY_HEX` before trusting any hash.\n2. Per-file SHA-256 check against the signed manifest. Mismatch → abort.\n3. TLS cert pinning on the hub connection (`.cert_pin`).\n\nA compromised hub cannot push a malicious update without also stealing the signing key, which lives offline of the hub.\n\n### Reporting vulnerabilities\n\nPlease email `security@aiterm.io` rather than opening a public issue for anything that could affect running installations.\n\n## CLI\n\n    aiterm status      # Connection status, paths, versions\n    aiterm start       # Start services\n    aiterm stop        # Stop services\n    aiterm restart     # Restart services\n    aiterm update      # Self-update (signed)\n    aiterm scan        # Scan local AI backends\n    aiterm logs [N]    # Tail last N log lines\n    aiterm uninstall   # Remove connector (confirmation required)\n\n## Configuration\n\n`connector.json` (mode `0600`) is generated at install time. Relevant keys:\n\n    {\n      \"hub_url\":        \"wss://www.aiterm.io/connector\",\n      \"hub_token\":      \"\u003cpaired token\u003e\",\n      \"default_cwd\":    \"/home/user\",\n      \"upload_dir\":     \"/opt/aiterm/uploads\",\n      \"max_upload_mb\":  20\n    }\n\nYou can point `hub_url` at your own hub if you run a compatible backend. The wire protocol is JSON-over-WebSocket (message types documented inline in `connector.py`).\n\n## Contributing\n\nPRs welcome — especially:\n\n- New AI backends (`AI_COMMANDS` in `pty-manager.py`, `scan()` in `connector.py`).\n- Systemd/launchd/Windows-service improvements.\n- Translations for the CLI wrapper.\n\nFor substantial changes please open an issue first so we can align.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faiterm-io%2Faiterm-connector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faiterm-io%2Faiterm-connector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faiterm-io%2Faiterm-connector/lists"}