Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2026 Hari Srinivasan
# SPDX-FileCopyrightText: 2026 Lokesh
# SPDX-License-Identifier: Apache-2.0

name: CI
Expand Down Expand Up @@ -27,6 +28,7 @@ jobs:
outputs:
code: ${{ steps.filter.outputs.code }}
workflows: ${{ steps.filter.outputs.workflows }}
relay: ${{ steps.filter.outputs.relay }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4
Expand All @@ -35,6 +37,7 @@ jobs:
filters: |
code:
- 'packages/**'
- 'services/**'
- 'distribution/**'
- 'scripts/**'
- 'package.json'
Expand All @@ -46,6 +49,12 @@ jobs:
- '.github/workflows/ci.yml'
workflows:
- '.github/workflows/**'
relay:
- 'services/relay/**'
- 'packages/protocol/src/remote-transport.ts'
- 'packages/protocol/test/fixtures/remote-transport-v1.json'
- 'packages/protocol/test/fixtures/internal-relay-api-v1.json'
- '.github/workflows/ci.yml'

quality:
name: Build and test
Expand Down Expand Up @@ -74,6 +83,41 @@ jobs:
if: needs.changes.outputs.code == 'true'
run: pnpm check

relay:
name: Relay build and test
runs-on: ubuntu-latest
timeout-minutes: 20
needs: changes
steps:
- name: No relay changes
if: needs.changes.outputs.relay != 'true'
run: echo "No relay changes detected; required check reports success."
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
if: needs.changes.outputs.relay == 'true'
- name: Set up Erlang and Elixir
if: needs.changes.outputs.relay == 'true'
uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1
with:
otp-version: 27.3.4.17
elixir-version: 1.18.5
- name: Restore Mix caches
if: needs.changes.outputs.relay == 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
services/relay/deps
services/relay/_build
~/.mix
key: relay-${{ runner.os }}-${{ hashFiles('services/relay/mix.lock') }}
- name: Fetch relay dependencies
if: needs.changes.outputs.relay == 'true'
working-directory: services/relay
run: mix deps.get --check-locked
- name: Format, compile, test, analyze, and audit relay
if: needs.changes.outputs.relay == 'true'
working-directory: services/relay
run: mix format --check-formatted && mix compile --warnings-as-errors && mix test && mix credo --strict && mix dialyzer && mix deps.audit

licenses:
name: REUSE licenses
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# SPDX-FileCopyrightText: 2026 Hari Srinivasan
# SPDX-FileCopyrightText: 2026 Lokesh
# SPDX-License-Identifier: Apache-2.0

node_modules/
dist/
.release/
coverage/
services/relay/_build/
services/relay/deps/
docs/screenshots/
*.tsbuildinfo
.env
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<!-- SPDX-FileCopyrightText: 2026 Hari Srinivasan -->
<!-- SPDX-FileCopyrightText: 2026 Lokesh -->
<!-- SPDX-License-Identifier: Apache-2.0 -->

# Axl development guide
Expand Down
21 changes: 15 additions & 6 deletions CODE_STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

Status: working plan. This document accompanies [ROADMAP.md](ROADMAP.md) and [OPEN_SOURCE.md](OPEN_SOURCE.md).

Updated: 2026-08-28
Updated: 2026-09-12

## 1. Keep everything in one repository

Expand All @@ -29,7 +29,8 @@ Codex offers a useful contrast. Its CLI and Rust core share a repository, while

## 2. Languages

- Use **TypeScript** for the kernel, protocol, daemon, adoption compiler, terminal client, web client, and extensions. It matches the ecosystems and standards Axl integrates with.
- Use **TypeScript** for the kernel, protocol, daemon, adoption compiler, terminal client, web client, extensions, and hosted control plane. It matches the ecosystems and standards Axl integrates with.
- Use **Elixir/OTP only for the hosted ciphertext relay** under `services/relay/`. The relay is a bounded transport process and must not own daemon, RPC, account, persistence, or cryptographic behavior.
- Use **Kotlin with Jetpack Compose** for Android and **Swift with SwiftUI** for iOS. Choose protocol code generation when the first of these clients is built.
- Do not add another application language. Tooling should use TypeScript or POSIX shell.

Expand All @@ -51,6 +52,9 @@ axl/
ui/ # shared presentation tokens and React renderers
sdk/ # shared TypeScript client SDK when multiple clients need it
extensions/ # first-party extensions, one package per feature (roadmap §2.9)
services/
control-plane/ # separately deployable TypeScript hosted control plane
relay/ # separately deployable Elixir/OTP opaque WebSocket relay
apps/
android/ # Gradle project using the generated Kotlin SDK
ios/ # Xcode project using the generated Swift SDK
Expand All @@ -65,8 +69,11 @@ These rules keep package ownership clear:
- `packages/protocol` has no runtime dependencies.
- `packages/kernel` depends only on `packages/protocol` and Node.js built-ins.
- First-party extensions use the same public extension API as third-party extensions.
- `packages/protocol` is the only source of wire-format truth. TypeScript definitions stay authoritative until a non-TypeScript client creates a real need for generation.
- `packages/protocol` is the only source of wire-format truth. TypeScript definitions stay authoritative until a non-TypeScript presentation client creates a real need for generation. The Elixir relay implements only its narrow transport and internal-service framing against canonical byte and JSON fixtures; it is not a daemon-protocol client.
- Apps use the public protocol SDK rather than package internals.
- `services/control-plane` may depend on `packages/protocol`. It owns hosted account, installation, device, ticket, prekey, grant, upload-reservation, quota, and security-audit mutation. Identity providers, persistent datastores, and production service authentication stay behind injected interfaces until approved.
- `services/relay` consumes versioned language-neutral fixtures. It must not import TypeScript package internals, access the control-plane datastore, decrypt envelopes, interpret daemon RPC, persist canonical history, or store attachment bodies. It calls the authenticated control-plane admission API once per new connection and accepts authenticated revocation notifications.
- The control plane and relay are separate deployables. They share no private implementation imports and communicate only through their versioned internal HTTP contract.
- `packages/runtime` assembles providers, tools, extensions, sandboxing, and the authoritative daemon without importing a presentation client.
- `packages/tui` is a daemon client projection. It does not construct the runtime or depend at runtime on sandbox, kernel, or concrete extension implementations. It may depend on the dependency-free public `@axl/extension-api` for client-local presentation customization.
- `packages/ui` owns shared presentation tokens and React renderers. It may depend only on `packages/sdk` and presentation libraries. It owns no daemon or process authority.
Expand All @@ -80,7 +87,7 @@ The protocol package owns the contract between the daemon and every client.

- TypeScript definitions are authoritative while all clients use TypeScript.
- A schema change requires prior design discussion and compatibility notes.
- The first Swift or Kotlin client triggers a decision on the schema language and generator.
- The first Swift or Kotlin client triggers a decision on the schema language and generator. The relay's bounded outer-frame parser does not trigger client SDK generation because it does not parse daemon RPC or canonical events.
- Generated SDKs then ship through their native package systems so external and in-tree clients use the same contract.

## 5. Independent implementation
Expand All @@ -91,8 +98,8 @@ Any approved adaptation records its source, commit, and changes in an SPDX heade

## 6. Build tools

- Use pnpm workspaces for package management. Add a task runner with remote caching only when repository scale justifies it.
- Keep Gradle and Xcode native. CI coordinates the build systems but the JavaScript toolchain does not wrap them.
- Use pnpm workspaces for TypeScript package and service management. Add a task runner with remote caching only when repository scale justifies it.
- Keep Mix native for `services/relay`, and keep Gradle and Xcode native. CI coordinates the build systems but the JavaScript toolchain does not wrap them.
- Version packages in `packages/` together. Mobile apps keep their own store versions.

Bazel would add more contributor cost than value at the current scale.
Expand All @@ -110,6 +117,8 @@ Bazel would add more contributor cost than value at the current scale.
Every required check reports a result. Path filters decide whether the full job runs or a small gate job reports that no relevant files changed.

- Kernel, protocol, and SDK changes run all builds, including both mobile apps.
- Control-plane changes run the root TypeScript checks and package-boundary checks.
- Relay or shared remote-fixture changes run Mix formatting, compilation with warnings as errors, tests, Credo, Dialyzer, dependency audit, cross-language fixture checks, package-boundary checks, and REUSE.
- App-only changes run that app and lint checks.
- Documentation and plan changes run formatting, link checking, and REUSE checks.
- CodeQL, Gitleaks, and dependency review run for every merge candidate.
Expand Down
23 changes: 22 additions & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2026 Hari Srinivasan
# SPDX-FileCopyrightText: 2026 Lokesh
# SPDX-License-Identifier: Apache-2.0

version = 1
Expand All @@ -11,7 +12,6 @@ path = [
"LICENSES/Apache-2.0.txt",
"biome.json",
"distribution/npm/package.json",
"package.json",
"packages/ai/tsconfig.build.json",
"packages/ai/tsconfig.json",
"packages/daemon/tsconfig.build.json",
Expand Down Expand Up @@ -42,6 +42,14 @@ path = [
SPDX-FileCopyrightText = "2026 Hari Srinivasan"
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = ["package.json"]
SPDX-FileCopyrightText = [
"2026 Hari Srinivasan",
"2026 Lokesh",
]
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = [
"packages/ai/package.json",
Expand All @@ -60,6 +68,19 @@ SPDX-FileCopyrightText = [
]
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = [
"packages/protocol/test/fixtures/internal-relay-api-v1.json",
"packages/protocol/test/fixtures/remote-transport-v1.json",
"services/control-plane/package.json",
"services/control-plane/tsconfig.build.json",
"services/control-plane/tsconfig.json",
"services/relay/.tool-versions",
"services/relay/mix.lock",
]
SPDX-FileCopyrightText = "2026 Lokesh"
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = ["NOTICE"]
SPDX-FileCopyrightText = [
Expand Down
18 changes: 18 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,10 @@ Requirements:

The current mobile plan favors SwiftUI on iOS and Jetpack Compose on Android because native code supports Live Activities, Android foreground services, notification actions, widgets, share sheets, and efficient streaming text. This is not a binding stack decision. Choose the implementation when mobile work begins and its requirements are concrete.

Remote transport uses pairwise application-level E2EE in addition to TLS. The approved direction is PQXDH for asynchronous session establishment and Triple Ratchet for ongoing messages. This direction supersedes any earlier Noise selection. Production cryptography remains blocked on Person 1's security RFC, exact suite, reviewed library, secure-state design, interoperability fixtures, and independent security review. Transport code treats encrypted envelopes and public prekey bundles as bounded opaque bytes. The relay never imports the E2EE implementation or decrypts traffic. The proposed remote action-binding and approval rules are in [`docs/architecture/remote-permission-authorization.md`](docs/architecture/remote-permission-authorization.md); that draft does not enable remote approval.

The managed path uses two separately deployable services: the TypeScript control plane owns hosted state and one-use admission, while the Elixir/OTP relay owns bounded in-memory WebSocket routing. The daemon remains the command and session authority. Transport proof uses only disposable sessions, a deterministic fake provider, opaque fixtures, and a test-only fake E2EE adapter. Ordinary-session steering and remote permission approval remain disabled until the E2EE and release gates pass.

#### 16.4 Headless and automation

The same daemon serves non-interactive callers:
Expand Down Expand Up @@ -2348,6 +2352,20 @@ The shared remote-connectivity and remote-web subsections are a scoped sequencin
- [ ] Keep disconnected input as an explicit draft until the daemon durably accepts it; do not create a browser-authoritative prompt queue.
- [ ] Support existing-session observation and steering first. Require a daemon-owned approved workspace identifier before creating a remote Code session.

The transport-first remote-control slice is an approved exception to phase ordering. It may establish service boundaries, opaque framing, one-use ticket admission, bounded relay routing, daemon authorization behind a test-only fake E2EE adapter, and reusable SDK delivery machinery. It must not implement cryptography, select production identity or storage infrastructure, enable ordinary-session remote access, or advertise production remote control.

The private slice was created from clean `main` commit `ea906d0295ba67f833c49ace408a9573551ea687` and rebased for integration onto clean `main` commit `57bd31b7e718a125fc51a0fcf3a554cb100ea708` on `feature/e2ee-transport`. Stop for architecture review after the documentation, separate service boundaries, versioned fixture contract, atomic ticket-consumption path, and first bounded relay slice land.

#### Remote transport preflight

- [x] Record PQXDH plus Triple Ratchet as the approved direction and keep exact production cryptography blocked on Person 1's reviewed contract and library.
- [x] Add the separately deployable TypeScript control plane under `services/control-plane/` with authenticated ticket issuance and atomic one-use consumption through injected interfaces.
- [x] Add the separately deployable Elixir/OTP relay under `services/relay/` with authenticated admission, opaque bounded framing, in-memory installation-scoped routing, backpressure, heartbeat, lease, revocation, and draining behavior.
- [x] Publish language-neutral admission, revocation, and exact binary accept/reject fixtures consumed by both implementations.
- [x] Run TypeScript and Mix formatting, compilation, tests, static analysis, dependency auditing, package-boundary, and SPDX/REUSE checks in CI.
- [x] Draft the daemon-owned remote permission action-binding contract without enabling it.
- [x] Stop at the architecture checkpoint before daemon, SDK, prekey, attachment, or production integration work.

#### Mobile clients

- [ ] Choose mobile implementation stacks when work begins, based on concrete platform and product requirements.
Expand Down
Loading
Loading