scaffold: AGENTS.md, Cargo.toml, licenses, agent conventions adapted for alktunnels
- AGENTS.md adapted from alkcall (via alktty's pattern) for the tunnel protocol crate: substrate-agnostic conventions, two-pump contract, no-forced-binding requirement, alkcall 0.4.0 dependency, wasm-clean default crate, ALPN naming - .opencode/agents/implementation-specialist.md conventions section adapted from alkcall's call-protocol rules to tunnel-crate rules (mirroring alktty's adaptation) - docs/sdd_process.md package reference fixed to alktunnels - Cargo.toml scaffold: wasm-clean tokio subset, alkcall 0.4.0 - LICENSE-MIT / LICENSE-APACHE copied from alktty - src/lib.rs protocol-only stub; docs/architecture/ lands in Phase 1 Verification: cargo test (0 tests, ok), cargo clippy --all-targets -- -D warnings, cargo fmt --check, cargo clippy --target wasm32-unknown-unknown -- -D warnings — all clean
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
target/
|
||||
node_modules/
|
||||
.worktrees/
|
||||
@@ -210,62 +210,70 @@ This is especially important for complex tasks that span many file operations.
|
||||
Read `AGENTS.md` at project root for full details. Key rules:
|
||||
|
||||
1. **No comments in code** — Per project convention. Doc comments (`///`, `//!`)
|
||||
are fine and expected on public API.
|
||||
2. **Error handling** — `thiserror` for library error types. No panics in
|
||||
library code. No `unwrap()` or `expect()` outside tests. For poisoned
|
||||
`RwLock`/`Mutex`, use `unwrap_or_else(|e| e.into_inner())` so a panic in one
|
||||
operation does not cascade to other operations.
|
||||
3. **`tokio` is the async runtime** — all I/O is async. Use `tokio::sync`
|
||||
primitives (`oneshot`, `mpsc`) for request correlation and subscription
|
||||
channels; `parking_lot` for short-held internal locks (`PendingRequestMap`).
|
||||
4. **No secret material on the wire** — `call.requested`/`call.responded`
|
||||
payloads and `OperationContext.metadata` carry no private keys, API keys, or
|
||||
decrypted credentials. Outbound credentials flow through `Capabilities`
|
||||
injected at the assembly layer → `HandlerRegistration.capabilities` →
|
||||
`OperationContext.capabilities` → handler.
|
||||
5. **No-env-vars invariant** — no handler reads outbound credentials from any
|
||||
source other than `OperationContext.capabilities`. This is a spec-level
|
||||
invariant, not a runtime convention.
|
||||
6. **`OperationEnv` must remain a trait** — the trait-based design enables
|
||||
registry layering (session overlays, connection overlays, peer-keyed
|
||||
composition). Do not make it concrete or hardcode the global registry.
|
||||
7. **Wire formats are stable** — `EventEnvelope` (`{ type, id, payload }` +
|
||||
length-prefixed JSON framing) and the channels 8-byte chunk header
|
||||
(`[channel_id:u32 BE][length:u32 BE][payload]`) are one-way doors. New event
|
||||
types may be added; existing shapes must not change.
|
||||
8. **Producer/consumer, not server/client** — both sides of a call or channels
|
||||
are fine and expected on public API. Inline `//` comments only when the user
|
||||
asks or when a non-obvious safety/correctness constraint would otherwise be
|
||||
missed (e.g., "a two-pump tunnel must shut down the opposite sink on pump
|
||||
completion — `try_join!` alone deadlocks").
|
||||
2. **Error handling** — `thiserror` for library error types (`TunnelError`;
|
||||
`HandlerError`/`StreamError` come from alkcall::core). No panics in library
|
||||
code. No `unwrap()` or `expect()` outside tests. For poisoned
|
||||
`RwLock`/`Mutex`, use `unwrap_or_else(|e| e.into_inner())`.
|
||||
3. **`tokio` is the async runtime** — all I/O is async. The tunnel pumps, the
|
||||
channels integration, and the consumer session type are all async. Use
|
||||
`tokio::sync` primitives (`oneshot`, `mpsc`) for lifecycle correlation and
|
||||
per-direction data flow.
|
||||
4. **WASM target is load-bearing** — the default crate (protocol-only) MUST
|
||||
compile to `wasm32-unknown-unknown`. Use the wasm-clean tokio subset
|
||||
(`rt`, `sync`, `io-util`, `macros`, `time`); **do NOT use
|
||||
`features = ["full"]`**. Substrate backends (local TCP/UDP sockets, process
|
||||
listeners) are feature-gated and never imported from the
|
||||
shared/producer/consumer modules.
|
||||
5. **Wire format is stable** — the tunnel payload rides inside channels data
|
||||
channels as raw bytes (channels strips its 8-byte header transparently; the
|
||||
tunnel protocol owns whatever framing it puts inside the `BiStream`). Any
|
||||
negotiation/setup frame is self-contained (length-prefixed JSON per alktty
|
||||
ADR-006 precedent), not alkcall's `EventEnvelope` framing. Wire-format
|
||||
changes after the first consumer are additive-only.
|
||||
6. **Producer/consumer, not server/client** — both sides of a channels
|
||||
connection can initiate. Use "producer"/"consumer" or "accept side"/"connect
|
||||
side," not "server"/"client."
|
||||
9. **Vendored core types** — `Connection`, `ProtocolHandler`, `BiStream`,
|
||||
`BidiStreamSource`, `AuthContext`, `IdentityProvider`, `Identity`,
|
||||
`AuthToken`, `Capabilities`, `OwnershipProvider`, `HandlerError`,
|
||||
`StreamError` live in this crate. Do not add a separate `alkcore` dependency.
|
||||
Keep them lean (no TLS, no transport coupling, no endpoint/accept-loop).
|
||||
10. **BAST documents for wire formats** — every binary wire format carries a
|
||||
BAST (Binary Abstract Syntax Tree) document as its machine-readable spec
|
||||
(e.g. the channels chunk header's `docs/architecture/chunk-header.bast.json`,
|
||||
embedded as `CHUNK_HEADER_BAST`). BAST is plain JSON — no dependency
|
||||
required to author or consume it. The `alktype` crate compiles BAST into
|
||||
readers/writers/validators; future codegen derives language-specific
|
||||
implementations. Trivial or hot-path formats (chunk header, tty framing)
|
||||
stay hand-rolled with the BAST doc as the contract; complex formats (sftp)
|
||||
use the alktype engine or codegen. Do not roll your own offset map or
|
||||
validator for complex formats.
|
||||
11. **Feature flags** — transports may be feature-gated if the need arises. The
|
||||
base crate should compile lean (no `quinn`, no `iroh` unless the feature is
|
||||
on). Verify both `cargo test` (default) and `cargo test --all-features` pass
|
||||
if features are added.
|
||||
12. **Abort cascades to descendants** — `call.aborted` for a parent cascades to
|
||||
all non-terminal descendants. Default `abort-dependents`;
|
||||
`continue-running` opt-in. The composing handler decides the child's policy,
|
||||
not the wire caller.
|
||||
13. **Peer authorization via `AccessControl`** — a remote peer's call is
|
||||
authorized by `AccessControl::check(peer_identity)`. No `remote_safe` flag,
|
||||
no `trusted_peer` bypass. `Visibility::Internal` ops are never wire-callable.
|
||||
7. **Substrate-agnostic by construction** — the protocol layer must not know
|
||||
whether bytes come from TCP, UDP, a Unix socket, or stdio. Target
|
||||
addressing, direction, and lifecycle bookkeeping must not hardcode a
|
||||
substrate.
|
||||
8. **Two-pump shutdown-on-completion is a contract** — each pump MUST shut
|
||||
down the opposite sink when it completes; `tokio::try_join!` alone
|
||||
deadlocks (POC-validated, alknet ADR-078). Emit EOF sentinels (zero-length
|
||||
chunks) on clean sink shutdown.
|
||||
9. **No forced local binding** — a tunnel must not require the producer (or
|
||||
consumer) to bind a local port. Support both SSH `-L` and `-R` style
|
||||
directions and unbound/listen-optional flows; the binding decision belongs
|
||||
to the caller.
|
||||
10. **Backpressure and limits are inherited** — bounded per-channel buffers,
|
||||
the 256-channel cap, monotonic IDs, and zero-length-sentinel EOF are
|
||||
alkcall channels invariants. Do not build a second demux/mux or re-derive
|
||||
limits.
|
||||
11. **Vendored core types come from alkcall** — `Connection`,
|
||||
`ProtocolHandler`, `BiStream`, `BidiStreamSource`, `AuthContext`,
|
||||
`Identity`, `IdentityProvider`, `AccessControl`, `OwnershipProvider`,
|
||||
`HandlerError`, `StreamError` come from `alkcall::core`. Do not vendor
|
||||
copies. Pin `alkcall = "0.4.0"`; bump deliberately; fix issues upstream.
|
||||
12. **BAST document for the wire format** — binary framing (if any beyond
|
||||
pass-through) carries a BAST document under `docs/architecture/`
|
||||
conforming to `https://alk.dev/bast/v1/schema`. alktunnels does not depend
|
||||
on alktype; hand-rolled codecs are fine for trivial formats.
|
||||
13. **Access control** — scope-gate tunnel opens (`TUNNEL_OPEN_SCOPE`, shape
|
||||
following alktty's `TTY_OPEN_SCOPE`); the channels path gets authorization
|
||||
via `ChannelCore::register_openable`, which runs the ACL before the open
|
||||
handler. Optionally consult `OwnershipProvider`
|
||||
(`provider.owns(id_ref, kind, &id, "tunnel")` — the 4-arg shape).
|
||||
14. **Naming conventions** — Rust standard: `snake_case` for functions/variables/
|
||||
modules, `PascalCase` for types/traits, `SCREAMING_SNAKE_CASE` for constants.
|
||||
15. **Module structure** — one module per file under `src/`, re-exported from
|
||||
`src/lib.rs`. Public API surface is `lib.rs` re-exports.
|
||||
`src/lib.rs`. Public API surface is `lib.rs` re-exports. Producer half
|
||||
(adapter / open-handler), consumer half (typed session/client), shared
|
||||
wire/target-addressing modules; backends feature-gated and never imported
|
||||
from shared/producer/consumer modules.
|
||||
|
||||
## Key Principles
|
||||
|
||||
|
||||
288
AGENTS.md
Normal file
288
AGENTS.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# AGENTS.md
|
||||
|
||||
Operating instructions for opencode agents working in this repo. opencode
|
||||
auto-loads this file as instructions, overriding the built-in defaults for
|
||||
this project. Custom agents in `.opencode/agents/` inherit these rules
|
||||
unless their own prompts say otherwise.
|
||||
|
||||
## Git Workflow
|
||||
|
||||
**Commit and push when reasonable.** When a change is complete and
|
||||
verified (build + lint + tests pass), commit and push to `origin/main`
|
||||
without asking. This overrides the built-in default of "only commit when
|
||||
explicitly asked."
|
||||
|
||||
The workflow:
|
||||
|
||||
1. Make the change
|
||||
2. Verify: `cargo test`, `cargo clippy --all-targets -- -D warnings`,
|
||||
`cargo fmt --check`, `cargo doc --no-deps` if docs changed
|
||||
3. Inspect `git status` and `git diff` before staging — stage only the
|
||||
intended files, never secrets
|
||||
4. Write a concise commit message matching the repo style (see `git log
|
||||
--oneline -10`). For multi-point changes, use a summary line plus a
|
||||
body with bullet points and a verification block.
|
||||
5. `git push origin main`
|
||||
6. Report the commit hash and the verification summary
|
||||
|
||||
Exceptions — **do not** commit or push without asking:
|
||||
|
||||
- The change is exploratory / speculative (you're not sure the user wants
|
||||
it kept)
|
||||
- The user is actively reviewing the diff and may ask for changes
|
||||
- The change touches the wire format or a trait shape that backends or
|
||||
consumers implement (one-way doors — see "Wire formats are stable" and
|
||||
"Backend trait shapes are one-way doors" below; once consumers exist,
|
||||
those signatures are wire-stable contracts)
|
||||
- You'd be force-pushing, amending a published commit, creating an empty
|
||||
commit, or skipping hooks
|
||||
|
||||
Never commit secrets, keys, or credentials. If a commit fails or hooks
|
||||
reject it, fix the issue and create a new commit — do not amend the
|
||||
failed one.
|
||||
|
||||
Git identity is preconfigured (`glm-5.3-flash <glm-5.3-flash@alk.dev>`). Do
|
||||
not change `git config`, skip hooks, or use `git commit -i`.
|
||||
|
||||
## Project Conventions (Rust / tunnel protocol crate)
|
||||
|
||||
This is the tunnels protocol crate — arbitrary bidirectional tunnels in
|
||||
the `ssh -L` / `ssh -D` sense (TCP, UDP, unix sockets, and other stream
|
||||
or datagram substrates), riding alkcall channels the same way alktty
|
||||
does. It is the sibling of alktty (`alk/tty` — terminal sessions) and
|
||||
alkcall (call + channels — the substrate both build on). Where alktty
|
||||
multiplexes one service with a fixed five-stream channel structure, this
|
||||
crate generalizes the tunnel handler shape: the producer side dials or
|
||||
accepts on some local substrate and pumps bytes to/from a channels data
|
||||
channel; the consumer side bridges its own substrate onto that channel.
|
||||
The conventions below apply to all work in `src/` and `tests/`. They
|
||||
mirror `.opencode/agents/implementation-specialist.md` §Project
|
||||
Conventions and are repeated here so they apply to every session, not
|
||||
just spawned implementation agents.
|
||||
|
||||
1. **No comments in code** unless the user explicitly asks. This is a
|
||||
project-wide convention. Doc comments (`///`, `//!`) are fine and
|
||||
expected on public API. Inline `//` comments only when the user asks
|
||||
or when a non-obvious safety/correctness constraint would otherwise be
|
||||
missed (e.g., "a two-pump tunnel must shut down the opposite sink on
|
||||
pump completion — `try_join!` alone deadlocks; see alkcall's
|
||||
channels-adapter spec / alknet ADR-078").
|
||||
|
||||
2. **Error handling** — `thiserror` for library error types
|
||||
(`TunnelError`; `HandlerError`/`StreamError` come from
|
||||
`alkcall::core`). No panics in library code. No `unwrap()` or
|
||||
`expect()` outside tests. If you reach for `unwrap`, the error path
|
||||
wasn't specified — stop and decide what should actually happen. For
|
||||
poisoned `RwLock`/`Mutex`, use `unwrap_or_else(|e| e.into_inner())`
|
||||
so a panic in one operation does not cascade to other operations.
|
||||
|
||||
3. **`tokio` is the async runtime** — all I/O is async. The tunnel
|
||||
pumps, the channels integration, and the consumer session type are
|
||||
all async. Use `tokio::sync` primitives (`oneshot`, `mpsc`) for
|
||||
lifecycle correlation and per-direction data flow. Any non-wasm
|
||||
backend module (local TCP/UDP sockets, process-spawned listeners)
|
||||
lives behind a feature flag like alktty's `local`.
|
||||
|
||||
4. **WASM target is load-bearing (by default)** — the default crate
|
||||
(protocol-only) should compile to `wasm32-unknown-unknown`, following
|
||||
alktty's precedent. This is what keeps the downstream TS/Python
|
||||
adapter story viable (a wasm-compiled alktunnels is the protocol
|
||||
layer for a sandboxed adapter). Use the wasm-clean tokio subset
|
||||
(`rt`, `sync`, `io-util`, `macros`, `time`) — **do NOT use `features
|
||||
= ["full"]`** — and keep socket/platform I/O feature-gated. If a
|
||||
design decision ever forces the default crate off wasm (e.g. the
|
||||
wire format embeds a substrate-specific type), that is an ADR-worthy
|
||||
decision, not an accident.
|
||||
|
||||
5. **Wire format is stable** — the tunnel wire format is a one-way door
|
||||
once consumers exist. Follow the alknet ADR-093/alktty ADR-008
|
||||
precedent: the tunnel payload rides inside channels data channels as
|
||||
raw bytes (channels strips its 8-byte header transparently; the
|
||||
tunnel protocol owns whatever framing it puts inside the
|
||||
`BiStream`). If a negotiation/setup frame is needed (like alktty's
|
||||
`NegotiateRequest`), make it self-contained per alktty ADR-006 —
|
||||
length-prefixed JSON, not alkcall's `EventEnvelope` framing. Any
|
||||
wire-format ADR must be written before the first consumer exists;
|
||||
after that, changes are additive-only.
|
||||
|
||||
6. **Producer/consumer, not server/client** — both sides of a channels
|
||||
connection can initiate. A producer exposes tunnels (registers
|
||||
openable channels via `ChannelCore::register_openable`); a consumer
|
||||
opens tunnel channels (`ChannelClient`). Both sides can be both
|
||||
simultaneously — connection direction (who opened it) is independent
|
||||
of tunnel direction (who dials the target, who serves it). Avoid
|
||||
"server" and "client" framing in docs and API names; use "producer"
|
||||
and "consumer," or "accept side" / "connect side" for the
|
||||
connection-establishment half specifically. See alkcall ADR-022,
|
||||
ADR-037.
|
||||
|
||||
7. **Substrate-agnostic by construction** — the protocol layer must not
|
||||
know whether the bytes come from TCP, UDP, a Unix socket, or a
|
||||
stdio pipe. The handler sees a `Connection`/`BiStream` from alkcall
|
||||
core and pumps bytes; the substrate-specific types (`TcpStream`,
|
||||
`UdpSocket`, etc.) are confined to backend modules behind feature
|
||||
flags, injected at the assembly layer — the same inversion-point
|
||||
pattern as alktty's `TtyBackend`. "Arbitrary tunnels" means the
|
||||
bookkeeping (target addressing, direction, lifecycle) must not
|
||||
hardcode a substrate. See alknet ADR-078 §Where the pattern lives
|
||||
and the channels-adapter spec.
|
||||
|
||||
8. **Two-pump shutdown-on-completion is a contract** — a tunnel is the
|
||||
canonical two-pump handler (one pump per direction). Each pump MUST
|
||||
shut down the opposite sink when it completes; `tokio::try_join!`
|
||||
alone deadlocks. This was POC-validated in the alknet-channels POC
|
||||
(Target 3) and pinned as alknet ADR-078. The channels layer drops
|
||||
all per-channel senders on transport EOF — rely on that for
|
||||
teardown, and emit EOF sentinels (zero-length chunks) on clean
|
||||
sink shutdown.
|
||||
|
||||
9. **No forced local binding** — a tunnel must not require the producer
|
||||
(or consumer) to bind a local port. The POC's TCP-tunnel shape
|
||||
dialed a target from the handler; SSH `-R` style flows need the
|
||||
reverse (the remote side asks the local side to dial or listen).
|
||||
The API surface must support both directions and unbound/listen-
|
||||
optional flows; the binding decision belongs to the caller
|
||||
(assembly layer), not the protocol crate. This is a spec-level
|
||||
requirement for the architecture docs, not just an implementation
|
||||
preference.
|
||||
|
||||
10. **Backpressure and limits are inherited, not redefined** — bounded
|
||||
per-channel buffers, the 256-channel per-connection cap, monotonic
|
||||
channel IDs, and the zero-length-sentinel EOF convention are
|
||||
alkcall channels invariants (see alkcall ADR-040/041/034). The
|
||||
tunnel crate consumes them via `ChannelCore`/`ChannelClient`; do
|
||||
not build a second demux/mux or re-derive limits. Datagram
|
||||
substrates (UDP) map onto the chunk stream with the same
|
||||
backpressure; datagram-boundary preservation, if required, is a
|
||||
wire-format decision for an ADR, not an accident.
|
||||
|
||||
11. **Vendored core types come from alkcall** — `Connection`,
|
||||
`ProtocolHandler`, `BiStream`, `BidiStreamSource`, `AuthContext`,
|
||||
`Identity`, `IdentityProvider`, `AccessControl`,
|
||||
`OwnershipProvider`, `HandlerError`, `StreamError` come from
|
||||
`alkcall::core`. Do not vendor copies into this crate. alkcall is
|
||||
v0.4.x — breaking changes are expected at this major-zero stage;
|
||||
this is an early consumer, so we find and fix issues upstream
|
||||
rather than working around them. Pin `alkcall = "0.4.0"` (lockfile
|
||||
resolves 0.4.1) and bump deliberately.
|
||||
|
||||
12. **BAST document for the wire format** — when the tunnel wire format
|
||||
gains binary framing (if any beyond pass-through), it carries a
|
||||
BAST (Binary Abstract Syntax Tree) document as its machine-readable
|
||||
spec under `docs/architecture/`, conforming to the BAST meta-schema
|
||||
at `https://alk.dev/bast/v1/schema`. BAST is plain JSON — no
|
||||
dependency required to author or consume it. alktunnels does not
|
||||
depend on alktype; hand-rolled codecs are fine for trivial formats,
|
||||
with the BAST doc as the contract. Do not roll your own offset map
|
||||
or validator for complex formats — use alktype (as an optional dep)
|
||||
when that surfaces.
|
||||
|
||||
13. **Access control** — the producer's openable channels get
|
||||
authorization for free via `ChannelCore::register_openable`, which
|
||||
wires `AccessControl` into the operation spec (the registry runs
|
||||
the ACL before the open handler). Scope-gate tunnel opens (e.g.
|
||||
`TUNNEL_OPEN_SCOPE`, shape following alktty's `TTY_OPEN_SCOPE`) and
|
||||
optionally consult `OwnershipProvider` for resource ownership
|
||||
(`provider.owns(id_ref, kind, &id, "tunnel")` — the 4-arg shape;
|
||||
`OwnershipStore::record` is the 3-arg shape). Tunnels reach local
|
||||
networks — treat the open gate as the security boundary. See
|
||||
alkcall ADR-024, ADR-050.
|
||||
|
||||
14. **Feature flags** — substrate backends may be feature-gated if the
|
||||
need arises. The base crate should compile lean (no socket/platform
|
||||
deps unless the feature is on). Verify both `cargo test` (default)
|
||||
and `cargo test --all-features` pass if features are added.
|
||||
|
||||
15. **Naming** — Rust standard: `snake_case` for functions/variables/
|
||||
modules, `PascalCase` for types/traits, `SCREAMING_SNAKE_CASE` for
|
||||
constants.
|
||||
|
||||
16. **Module structure** — one module per file under `src/`, re-exported
|
||||
from `src/lib.rs`. Public API surface is `lib.rs` re-exports. The
|
||||
expected shape (to be finalized in the architecture docs): a
|
||||
producer half (adapter / open-handler for `alk/tunnel`-family
|
||||
ALPNs), a consumer half (a typed session/client that opens tunnel
|
||||
channels), and shared wire/target-addressing modules. Backend
|
||||
modules are feature-gated and never imported from the
|
||||
shared/producer/consumer modules.
|
||||
|
||||
17. **ALPN naming** — this crate owns the `alk/tunnel`-family ALPN(s).
|
||||
One ALPN per protocol per alkcall ADR-004; keep the `alk/` prefix
|
||||
convention. If multiple tunnel flavors need distinct ALPNs (e.g.
|
||||
stream vs datagram), decide via ADR before the first consumer —
|
||||
ALPN strings are wire-stable once published.
|
||||
|
||||
## Verification Commands
|
||||
|
||||
Run these before committing. All must pass.
|
||||
|
||||
```bash
|
||||
cargo test # full suite
|
||||
cargo clippy --all-targets -- -D warnings
|
||||
cargo fmt --check
|
||||
cargo doc --no-deps # if docs changed
|
||||
cargo test --all-features # if features are added
|
||||
cargo check --target wasm32-unknown-unknown # default crate stays wasm-clean
|
||||
cargo clippy --target wasm32-unknown-unknown -- -D warnings
|
||||
cargo publish --dry-run --allow-dirty # before a release
|
||||
```
|
||||
|
||||
The wasm check is the structural guard for the "default crate is
|
||||
wasm-clean" invariant (same as alktty) — run it whenever a
|
||||
non-backend module changes.
|
||||
|
||||
## Architecture Context
|
||||
|
||||
- `docs/architecture/` — will hold the authoritative spec (Phase 1 of
|
||||
the SDD process). It does not exist yet; Phase 0 research lives in
|
||||
`docs/research/`. Read it before non-trivial changes once it exists.
|
||||
- The prior art for this crate:
|
||||
- **alknet-channels POC** —
|
||||
`/workspace/@alkdev/alknet/docs/research/alknet-channels/poc-summary.md`
|
||||
(§POC Target 3: tunnel handler). Validates the demux→Connection→
|
||||
handler→mux path and the two-pump tunnel shape over a stand-in
|
||||
transport. POC code at `/workspace/alknet-channels-poc/`.
|
||||
- **alkcall** — `/workspace/@alkdev/alkcall` (v0.4.x, crates.io).
|
||||
The substrate: call protocol + channels multiplexing. The tunnel
|
||||
crate consumes `alkcall::core` types and the channels
|
||||
`ChannelCore`/`ChannelClient`/`register_openable` surface.
|
||||
- **alktty** — `/workspace/@alkdev/alktty` (v0.1, crates.io). The
|
||||
sibling pattern to follow: a producer/consumer protocol crate on
|
||||
alkcall channels, with the backend inversion point, wasm-clean
|
||||
default crate, and feature-gated local backend.
|
||||
- Key upstream ADRs that inform this crate's design (alknet numbers;
|
||||
alkcall's ports live in `/workspace/@alkdev/alkcall/docs/architecture/
|
||||
decisions/`):
|
||||
- alknet ADR-093 / alkcall ADR-035 — channels pure channel
|
||||
multiplexing (8-byte header, no `stream_type`); the tunnel payload
|
||||
is raw bytes inside the `BiStream`
|
||||
- alknet ADR-078 — two-pump shutdown-on-completion (the tunnel
|
||||
handler pattern; a helper extraction was deferred until a second
|
||||
two-pump consumer exists — this crate is that second consumer)
|
||||
- alknet ADR-074 / alkcall ADR-038 — `ChannelConnection` as a
|
||||
`BidiStreamSource`; every handler (TTY, tunnel, call) receives a
|
||||
`Connection`
|
||||
- alknet ADR-075 / alkcall ADR-039 — `ChannelsAdapter` /
|
||||
`ChannelManager` (substrate-agnostic demux; `params` is
|
||||
ALPN-specific — for tunnels, the target resource)
|
||||
- alknet ADR-071 / alkcall ADR-034 — channels wire format (one-way
|
||||
door)
|
||||
- alkcall ADR-037 — channel lifecycle operations (`channel/open` on
|
||||
channel 0; `params` carries the tunnel target)
|
||||
- alkcall ADR-042 — hub relay (byte-for-byte data-channel forwarding
|
||||
with ID rewrite — tunnels traverse relays transparently)
|
||||
- alknet ADR-085 — workspace scope: `alknet/tunnel` was flagged
|
||||
"POC-validated, minimal spec needed, not yet specced"; this crate
|
||||
is that spec
|
||||
- What the POC does NOT settle (open work for Phase 0/1):
|
||||
- UDP and Unix-socket substrates (the POC only exercised TCP) — the
|
||||
pump pattern is expected to generalize, but datagram boundary
|
||||
preservation and addressing bookkeeping are unspecced
|
||||
- The "no forced local binding" requirement (SSH `-R`/dynamic flows)
|
||||
is not covered by the POC at all
|
||||
- Target addressing format (what a tunnel `params` looks like) —
|
||||
alknet ADR-071 §ALPN table noted `alknet/tunnel` as `[0, 1]` data
|
||||
in/out only, but the addressing scheme was never decided
|
||||
- If a TODO references a design direction that an ADR has since decided
|
||||
against, the TODO is stale — remove it and align with the ADR. Do not
|
||||
implement the rejected design.
|
||||
1274
Cargo.lock
generated
Normal file
1274
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
Cargo.toml
Normal file
32
Cargo.toml
Normal file
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "alktunnels"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Arbitrary tunnel protocol: bidirectional TCP, UDP, and unix-socket tunnels over alkcall channels. Producer/consumer protocol crate."
|
||||
repository = "https://git.alk.dev/alkdev/alktunnels"
|
||||
readme = "README.md"
|
||||
keywords = ["tunnel", "proxy", "channels", "alkcall", "network"]
|
||||
categories = ["network-programming", "asynchronous"]
|
||||
exclude = [".opencode/", "AGENTS.md", "docs/reviews/", "docs/research/", "docs/plans/", "docs/sdd_process.md"]
|
||||
|
||||
[lib]
|
||||
name = "alktunnels"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
alkcall = "0.4.0"
|
||||
tokio = { version = "1", default-features = false, features = ["rt", "sync", "io-util", "macros", "time"] }
|
||||
bytes = "1"
|
||||
futures = "0.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
async-trait = "0.1"
|
||||
tracing = "0.1"
|
||||
thiserror = "2"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["full", "test-util", "macros"] }
|
||||
192
LICENSE-APACHE
Normal file
192
LICENSE-APACHE
Normal file
@@ -0,0 +1,192 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name), or refer to, the Work.
|
||||
(Note: Derivative Works shall not include works that remain separable from,
|
||||
or merely link (or bind by name) to the interfaces of, the Work and
|
||||
Derivative Works thereof.)
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2025-2026 Alk Development
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
21
LICENSE-MIT
Normal file
21
LICENSE-MIT
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025-2026 Alk Development
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This document defines the SDD process for the @alkdev/alkcall package. It
|
||||
This document defines the SDD process for the @alkdev/alktunnels package. It
|
||||
leverages:
|
||||
|
||||
- **OpenCode CLI** as the agent execution environment
|
||||
|
||||
5
src/lib.rs
Normal file
5
src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
//! alktunnels — arbitrary bidirectional tunnels over alkcall channels.
|
||||
//!
|
||||
//! Protocol-only scaffold. The producer half (tunnel open-handler),
|
||||
//! consumer half (typed session), and shared wire/target-addressing
|
||||
//! modules land with the Phase 1 architecture spec.
|
||||
Reference in New Issue
Block a user