Reviewer: Claude (Opus 4.8). Companion to the vision sanity-check
(2026-06-28-vision-sanity-check.md, idea-level) and the slice-1
code assessment (2026-06-28-slice-1-claude-adversarial-assessment.md).
Filed but not addressed. Seven GTM findings:
- G1: revenue vectors are a sequence (OSS → contract → hosted), not a menu
- G2: do-now, irreversible — secure CLA + commercial dual-license before
contributor #2 (directly affects ADR-0004; gates monetization doors)
- G3: the consulting trap
- G4: hosted = BYO-cloud, not multi-tenant SaaS
- G5+: the two near-free pivots — reposition as a voice-agent substrate
and beachhead on outbound-compliance
Cross-cutting with the vision-sanity-check's reframe (moat = boundary +
data loop, not determinism). Addressing these is strategy-doc + ADR
work that warrants user signoff; not auto-applied alongside the
code-track fixes.
Reviewer: Claude (Opus 4.8). Filing the strategy/vision-level critique
(for the slice-1 code-review see the companion
2026-06-28-slice-1-claude-adversarial-assessment.md already committed
in 8680f2b).
Filed but not addressed. Six strategic findings:
- S1: wedge leads with determinism (its most contestable leg)
- S2: differentiation valley between spearhead-end and capability ladder
(the load-bearing critique)
- S3: compliance paradox — regulated buyers won't move on bus-factor-1
- S4: turn-detection ownership at the tap — OpenAI Realtime has its own
server-side VAD by default; ARCHITECTURE.md says core-authoritative.
Belongs in the slice-3 (brain) spec as an explicit decision.
- S5: solo scope vs. category timing window
- S6: self-improvement loop is the actual moat, under-sold
Addressing these is strategy-doc work that warrants user signoff and
architecture pre-reading per AGENTS.md; not auto-applied alongside the
code-track fixes.
Apply findings from the Claude adversarial assessment
(docs/reviews/2026-06-28-slice-1-claude-adversarial-assessment.md)
after GLM-5.2 verification against the codebase + str0m 0.21 source.
Spec-only patches (code fixes follow as atomic commits):
- §1.2: add F5 (resource ceiling) to out-of-scope (deferred to step 5).
- §3.4: pin both str0m inputs as mandatory (Input::Receive + Input::Timeout)
and the deadline-honoring sleep semantics (Option B from brainstorming).
Without Input::Timeout, str0m's clock froze during inbound silence;
consent checks stopped; the browser tore the call down before the 60s
idle timeout (F1).
- §4.5: clarify the idle timeout keys on MediaData only. STUN/DTLS keepalives
do not reset it. Spec already said 'no RTP'; wording now makes the
intent unambiguous so the deviation cannot return (F4).
- §5.1: drop dead Channel.created_at field. The idle timeout keys on
RtcSession.last_rtp_rx, not creation time. The field was never read (F6).
- §10 (new): record the review patches lineage + cleared findings (F8, F9).
Findings F8 and F9 cleared by reading str0m 0.21 source
(packet/payload.rs:91 rebases MediaTime to the negotiated Opus clock;
RFC 7587 fixes SDP at opus/48000/2 regardless of internal rate). Cleared
with the same standard the review used for 'Browser ICE gathering'.
See AGENTS.md slice-1 boundaries — no scope additions; this is ratification
of fixes for bugs the spec already prohibited or that the dev-loop surfaced.
Design record for the operator console — the downstream non-technical
operator's surface. **Not scheduled for current build;** lands after the
spearhead (steps 1-6) and the ACD/escalation rungs (capability ladder
rungs 1-2). The current build target is slice-2; the next build target
after slice-2 is slice-3 (the OpenAI Realtime adapter). An agent that
proposes building the console now is wrong — point it at spec §0.
Records the visual-brainstorming session's resolved decisions so they
don't get re-litigated at implementation time:
§1 Principles (load-bearing):
- GUI is a pure API client — **green-zone, never an insider** (ADR-0008);
holds no privilege a third party couldn't get from the public API.
- Config-as-code is the single source of truth; the UI is a reconciled
*view* (the k8s model — declarative desired-state in git, the engine
reconciles; console + CLI + git are views over it).
- Operator persona, not builder. The builder authors flows in **code +
an AI pair** (ARCHITECTURE 'DX spine'); this console is NOT the
authoring surface for the technical builder. **No drag-drop authoring
canvas** — a deliberately-closed decision (vision-revision §8).
- Ships in the batteries-included distro and the all-in-one image
(the reference GUI is bundled — 'boom' includes a usable console).
§2 IA — Ops Console shell: left nav + center panel + persistent live rail.
Dense single-screen; for an operator who lives in it. Live rail always
visible regardless of the selected section.
§3 Dashboards — Live Ops (default landing) + Quality (sibling view). Maps
the k8s split: Live Ops = live operational state; Quality = the data-owned
eval loop (capability ladder rungs 3→4).
§4 Flows editor — master/detail + a generated, navigable preview map.
The preview map is a *view* rendered from the config, not a drag-drop
authoring canvas. Earns its place by showing branching (the escalation
split) the linear stage-list flattens.
§5 Declarative flow schema (illustrative YAML; format is open). The config
the console edits via the API; the builder edits in git; the engine
reconciles. The map is generated from this structure.
§6 Other sections (conventional): Agents · Numbers · Queues · People ·
Recordings · Settings.
§7 Architecture: separate app, public API only; never reaches into the FOB.
Config writes go through the reconcile path. Live data rides the event
stream off Valkey (ADR-0005); never the 20 ms media loop.
§8 v1 vs. later scoping. §9 open decisions (flow config format, map
rendering depth, quality metrics, live transport, framework).
Cross-refs ADR-0002 (fused vertical + composable platform), ADR-0006
(WebRTC ingress / escalation UX), ADR-0007 (rented trunk / Numbers
config), ADR-0008 (FOB / green-zone — the UI is green-zone),
ARCHITECTURE.md 'DX spine' + 'GUI & extension architecture', PORT_PLAN §5
(contact-center domain).
Mechanical propagation of the crate rename ( rutster-signaling-sip →
rutster-trunk ) and the repo URL fix ( github.com/anomalyco →
git.adlee.work/alee ) through the documents that name them:
- docs/DEVELOPMENT.md: crate-layout sketch + stub-crate description.
- docs/superpowers/plans/2026-06-28-slice-1-webrtc-loopback.md: the
workspace members list in the plan's binding-values section —
repository URL updated.
- docs/superpowers/plans/2026-06-28-slice-2-agent-tap.md: workspace
members list in binding-values + crate-layout sketch in the file-
structure section (rutster-signaling-sip → rutster-trunk).
- docs/superpowers/specs/2026-06-28-slice-2-agent-tap-design.md: §2.1
workspace-layout sketch's STUB crate-row.
No content changes beyond the substitutions; the slice-2 spec/plan
body (protocol, TapAudioPipe, TapClient, TapEngine, lifecycle, done-
criteria) is untouched.
ADR-0007 (rent the trunk) + ADR-0008 (FOB/green-zone doctrine) propagation
into the narrative docs that orient contributors + readers. No code changes.
README:
- Wedge bullet 2: 'one secure auditable boundary' now lists media + local
reflexes + spend + tap + audit (was: trunk termination + media + spend).
Adds the honest caveat that PSTN media inside the boundary is the on-prem
*graduation* (ADR-0007), not a day-one claim.
- 'Is / isn't': 'isn't a TDM/PSTN-hardware PBX — *and not a SIP stack.*'
No first-party SIP (ADR-0007). PSTN reach is rented transport.
- Memory-safety pillar: 'rutster parses no SIP at all' — entire first-party
wire surface is WebRTC/RTP/SRTP + WebSocket tap/ingress, all memory-safe
Rust. The carrier-SIP interop tail lives outside the trust boundary.
- Spearhead step 5: 'Add a real phone number via rented transport' (was:
'Replace WebRTC ingress with a real PSTN trunk call'). Re-aims at the
AI-telephony frontier; no first-party SIP stack.
- References: 0007 supersedes 0003 in the highlighted ADR list.
ARCHITECTURE:
- New 'FOB and the green zone' section after the fused-vertical framing —
names the build-vs-reuse doctrine with the FOB member list + green-zone
member list, and restates memory-safety precisely ('FOB is 100%
memory-safe Rust; the green zone is trusted OSS kept outside the
boundary — not an over-claim that every byte is Rust').
- 'Inside the boundary': 'Carrier trunk — rented transport, not first-party'
replaces 'Trunk SIP termination — Rust-native.' PSTN audio arrives as
a media-leg ingress from a rented CPaaS raw-media fork or an out-of-tree
SBC for on-prem sovereignty.
- Biggest technical risk: 'No longer the SIP stack — because rutster no
longer builds one.' Retires ADR-0003's named schedule risk; redirects
to 'the reflex loop itself' (turn-taking, VAD-driven barge-in, jitter,
pacing) — which is also the differentiator.
PORT_PLAN:
- Design rule 4: 'rent it, don't own it' replaces ADR-0003's 'Rust-native
trunk SIP, no SBC shield.' ADR-0007 restores the rule's original instinct.
- SIP signaling (trunk) row: disposition flipped to 🔌 Rented / out-of-tree.
- Outbound registration row: disposition flipped to 🔌 Rented / out-of-tree
(handled by the rented transport or out-of-tree SBC; rutster parses no SIP).
- Spend/abuse engine row: 'co-located with call origination + the tap inside
the boundary' (was: 'co-located with trunk termination'). rutster mediates
both the provider call-control API and the brain tap — the brain never
holds the wire.
- Spearhead step 5: 'Add a real phone number via rented transport.'
- Open decisions: SIP line updated to 'Re-decided — ADR-0007.'
AGENTS.md:
- ADR list: adds 0007 and 0008 with annotations; marks 0003 as superseded
by 0007.
- Key decisions to respect: prepends the FOB/green-zone doctrine as the
*the* build-vs-reuse rule. 'When in doubt, default to green zone — the
FOB earns its members, it doesn't collect them. This is why the trunk is
rented (ADR-0007) and Valkey reused (ADR-0005), not rebuilt. Don't pull
green-zone plumbing into the core.'
ADR-0007 — Trunk/PSTN strategy: rent the transport, no first-party SIP stack.
Supersedes ADR-0003 (Rust-native trunk SIP). The 2026-06 strategic-relevance
review stress-tested ADR-0003 against the 2026 market + solo-build realities
and it did not survive: a trunk SIP/media core is the highest-cost,
lowest-differentiation square on the board (the perceived-quality battle and
the buying criteria both live above the transport), and no production-hardened
pure-Rust SIP stack exists to stand on (ezk is bus-factor-1 / pre-1.0;
str0m self-describes as 'not for production' for SIP).
rutster owns no SIP stack. PSTN reach is rented transport in three layers:
1. Primary (demo, most users) — CPaaS raw-media fork (Twilio Media Streams,
Telnyx). Media-leg ingress, core-as-server (parallel to WebRTC ingress
per ADR-0006). Use the raw-audio fork, *not* managed Voice-AI products
that would consume the reflex loop.
2. Graduation (on-prem) — out-of-tree SBC (Kamailio/FreeSWITCH/drachtio +
rtpengine) B2BUAs carrier SIP into rutster as clean RTP/tap media,
outside the trust boundary.
3. Never — a first-party Rust trunk SIP stack. rsip/ezk stay off the
critical path.
The only things rutster owns in Rust: the call model, the reflex loop, the
agent tap. Everything that touches a carrier is rented (layer 1) or
out-of-tree (layer 2).
ADR-0008 — The FOB and the green zone: the build-vs-reuse doctrine. Names
the boundary criterion implicit across ADRs 0002-0007 as one mechanical rule
every contributor (human or agent) applies the same way.
- FOB (build in Rust) — admitted only if it passes one of: hot path,
security-constitutive, differentiating. Current FOB: media termination
(rutster-media on str0m) · real-time reflexes (VAD/barge-in/jitter/pacing)
· call model (rutster-call-model) · agent tap (rutster-tap) · spend/abuse
gate (rutster-spend) · control API + state/bus trait (rutster).
- Green zone (reuse at arm's length) — its own process/container/trust
domain, never in the FOB's address space. Admitted when it fails all
three FOB tests AND a trusted, actively-maintained project already does
it well. Current green zone: Valkey (ADR-0005) · carrier trunk (ADR-0007)
· agent brain · reference GUI · object storage · KMS · OTel collector ·
container supervisor.
- 'Actively maintained' is the load-bearing gate: excludes a first-party
Rust SIP stack and webrtc-rs; admits Valkey and str0m-for-WebRTC.
- When in doubt, default to green zone. The FOB earns its members.
Restates the memory-safety pillar precisely: the FOB is 100% memory-safe
Rust; the green zone is trusted battle-tested OSS kept outside the boundary
— not an over-claim that every byte is Rust.
ADR-0003's status is flipped to Superseded with a forward-pointer to ADR-0007.
Kept as the historical record of the 'own trunk SIP + media termination in
Rust, no SBC shield' plan; reversed under the strategic-relevance review.
Patches the agent-tap design with seven findings from PM-side review
before the implementation plan gets written. Resolves invariant-breakers
and deferred-decision debt that would compound across steps 3-6.
- P1 (high): §8.5 #6 'byte-identical loop_driver.rs' was the wrong bar
for the seam test. Reworded: the trait-method *call sites* are
unchanged; impl bodies are free to differ. That's what 'the seam
held' actually means — the contract is the test.
- P2 (high): tap: Option<TapHandle> and ChannelState were parallel
state machines without an invariant tying them. Added an explicit
state-pair table: tap is Some iff state == Connected. Lookups tolerate
transient inconsistency; the source-of-truth is the pair, not either
field alone.
- P3 (medium): v1 wire format was host-endian (silent big-endian
hazard on paper). Nailed LE explicitly via i16::to_le_bytes /
i16::from_le_bytes. v2 can negotiate endianness if a big-endian
brain ever materializes; v1 contract is LE-only. Added a §3.4
byte-order invariant alongside the sample-count invariant.
- P4 (medium): wss:// URLs were accepted by schema but returned
501 at connect time (UX: 2 s into a call). Now rejected at
POST /v1/sessions with 400 + clear message — fail fast at
session-create. Updated §4.4, §7.1, §7.3, §9, and the decisions
table for consistency; removed all stale '501 at connect' claims.
- P5 (low): TapEngine spawn owner was unspecified. Added explicit
ownership: session_map::drive_all_sessions (the binary's poll
task) observes the Connected transition from run_poll_once and
spawns the engine. Keeps loop_driver.rs behaviorally unchanged
(the §8.5 #6 seam test still holds).
- P6 (low): 'two new deps' in §1.1 undercounted. Updated list:
tokio-tungstenite, futures-util, url, serde_json (if not already
pulled).
- P7 (low): ADR-0007 was 'post-implementation follow-up, not a
slice-2 deliverable.' Tightened: strongly recommended to land
alongside or immediately after slice-2 implementation — once
step 3 ships against an unpinned protocol, the wire shape will
silently drift. The spec lists the decisions ADR-0007 should
capture (LE byte order added to the list).
Slice 2 hardens the presumptive agent-tap shape from ARCHITECTURE.md §"Agent tap"
(PORT_PLAN §10 open decision) against a working implementation:
- Replace slice-1's in-process EchoAudioPipe with a real out-of-process brain
reached over WSS; core-as-client, no inbound tap port (ADR-0006 posture).
- Versioned JSON event protocol (v1) over WS text frames with base64 PCM.
Event-named + JSON-over-text-WS so the step-3 OpenAI-Realtime adapter is a
translation shim, not a gRPC-bridge project.
- Decoupled TapEngine (cold-path tokio task) owns the WS connection; TapAudioPipe
is a thin sync wrapper the AudioSource/AudioSink seam holds. RtcSession's media
loop is byte-identical to slice-1 — the seam-test payoff.
- Core-authoritative playout buffer (5-frame/100ms ring; drop-oldest on overflow,
silence on underflow) — the place where "brain proposes, core disposes" lives.
- Bounded-backoff reconnect (250ms→5s cap, infinite retries); Channel stays
Connected; stateless-brain reconnect contract.
- Both a Python reference echo brain (examples/echo_brain/, not in CI) and a
Rust echo brain crate (crates/rutster-tap-echo, the in-process test server
+ standalone dev binary).
- ws:// loopback-only enforced; wss:// URL accepted at schema, 501 at connect
(cert/mTLS impl deferred to step 6).
Out-of-scope table (§1.2) locks the spearhead sequencing: real brain (3),
barge-in/VAD-driven playout kill (4), PSTN trunk (5), spend cap (6), wss:// TLS
posture (6), binary PCM mode (future-rung), byte-endian negotiation (tracked
open decision §9).
Builds out the user-facing docs tree alongside the slice-1 build target.
Kept the implementer's planned Task 7 'Slice 1 dev loop' README section
untouched — these docs are the canonical destination for that pointer.
- docs/QUICKSTART.md: 5-min path to 'hear the echo' (libopus install,
cargo run, browser steps, troubleshooting, what's happening under the
hood).
- docs/DEVELOPMENT.md: dev loop — workspace layout, per-crate iteration,
running tests, the 20 ms loop / 'drop + observe' rule, slice-1
boundaries (what NOT to add yet).
- CONTRIBUTING.md (at repo root, conventional): trunk-based dev,
CI gates, commit message style, atomic commits, code style +
learner-facing documentation policy, terminology policy, PR workflow
+ review checklist, GPL-3.0-or-later license.
- README.md: add a Quickstart pointer at the top, a Documentation table
linking to every doc, and the slice-1 build-target status block.
uuid 1.x → getrandom 0.4.x requires edition = "2024", stabilized in Rust 1.85.
The Task 1 pin of 1.80 was written when uuid 1.x resolved to a getrandom that
still compiled on edition 2021; the resolution has shifted. The brief said
'confirm the latest stable at impl time' — bumping the pin is faithful to that.
Plan doc's Task 6 CI matrix also updated: [stable, "1.85"].
Cross-task correction in response to Task 2 implementer's DONE_WITH_CONCERNS
escalation; not a Task 2 code change.
Verified str0m 0.21 + opus 0.3.1 API surfaces via docs.rs subagents;
patched the plan with the real signatures and several design-level fixes.
API-surface patches (str0m 0.21, verified):
- Global Constraints: full verified API surface documented inline
(Rtc::new takes Instant; SdpOffer::from_sdp_string is the entry
point, NOT from_str_unchecked; add_local_candidate returns
Option<&Candidate>; Writer::write takes rtp_time not media_time;
MediaTime has no add(Duration) -> use mt + MediaTime::from(d);
payload_params returns impl Iterator; MediaData.data is Arc<[u8]>).
- Task 4 accept_offer: rewritten with from_sdp_string + correct error
mapping; RtcSessionError::SdpOffer changed to String (collapses parse
+ accept failures uniformly).
- Task 4 loop_driver: MediaTime::add -> + MediaTime::from(Duration);
media.data deref coercion documented.
Design-level patches:
- Task 4: RtcSession::new_for_test -> pub fn new() (single idiomatic
constructor, no test/prod split).
- Task 4: added accept_offer_transitions_channel_to_connecting test
(the transition was claimed but untested in the original plan).
- Task 5: dropped reqwest from workspace deps (unused — integration
test uses tower::ServiceExt::oneshot); added tower as workspace dep
+ binary crate dev-dependency.
- Task 5: removed duplicate DELETE /v1/sessions route (was incorrectly
chained via .delete() on the collection route AND on /v1/sessions/:id
— only the latter is correct).
- Task 5: clarified pub mod requirement (must be pub because the
integration test references modules via absolute paths).
- Global Constraints: added task/PR strategy (one commit per task,
merged in numeric order, granular history is load-bearing for the
learning-codebase goal).
Self-review section updated to reflect the patches.
Plan is now implementation-ready.
API-surface corrections (verified against str0m 0.21 docs):
- str0m::Live -> str0m::Rtc (no Live type exists)
- needs_input_timeout() -> Rtc::poll_output() returning Output::Timeout(Instant)
- RtcConfig::with_dtls_cert -> RtcConfig::set_dtls_cert (also: str0m auto-gens
a cert if none passed; slice 1 still passes explicitly for ownership clarity)
- Event::RtpPacket(pkt, rid) -> Event::RtpPacket(RtpPacket) { single struct,
RID is a field }
- str0m::ice::IceAgent -> no public 'ice' module; ICE types at crate root:
Candidate, CandidateKind, IceCreds, IceConnectionState
- str0m.write_rtp() on Rtclive -> Frame API path: Rtc::writer(mid) -> Writer::write
New design decision surfaced by API verification:
- Media API path: Frame API (default) vs RTP API (set_rtp_mode(true)).
Slice 1 uses Frame API — the proof target is the codec-to-PCM boundary,
not RTP packetization. RTP API is a candidate for step 5.
Design-level fixes from close reading:
- §3.3 AudioSource/AudioSink: clarify who drives the traits (the poll loop
drives both; sink on inbound MediaData, source on Output::Timeout deadline)
- §3.6: clarify cert/CrossRef — fresh in-memory cert each restart is safe
because browser fetches fresh SDP each session, no caching layer to go stale
- §4.1: add Content-Type: application/sdp to /offer (browser-side fetch needs
it)
- §4.5: resolve 5-min-vs-60s idle-timeout contradiction (60s, not 5 min)
- §4.5: add graceful-shutdown story (drop DashMap on SIGINT, browsers see
dead peer — acceptable for dev loop)
- §6.4: fix 'mocked str0m Rtc' — str0m's Rtc is a concrete struct, not a
trait; sans-IO means drive real Rtc with synthetic Inputs, not mock it.
Stronger than mocking: production path exercised exactly.
Resolves 5 load-bearing API errors + 3 design-level ambiguities. Spec is now
implementation-ready pending user review.
Folds the 2026-06-26 vision-revision pressure-test into the canonical
docs. The spec moves from Proposed to Ratified; the decisions it produced
land in the docs it said it would amend, with ADRs recording each
load-bearing reversal.
- README.md: reframe north star (AI-era contact-center engine, not
Asterisk successor); persona; revised pillars (add data-ownership,
demote WASM, promote spend-control); update 'what it is/isn't'.
- ARCHITECTURE.md: replace three-plane framing with fused per-call
vertical + composable horizontal platform; remove control<->media
hot-path gRPC hop; make the agent tap the central interface; add
DX spine + GUI-as-API-client + k8s declarative/operational model.
Also: 'too slow to police' -> 'too slow to enforce' (terminology).
- PORT_PLAN.md: recharacterize as capability checklist (not template);
graduate contact-center capabilities to first-class domain; Rust-native
trunk SIP rows; WASM demoted; thin-slice + capability ladder phasing.
- ADR-0001: marked Superseded by ADR-0003.
- ADR-0002 (new): north star + fused per-call core.
- ADR-0003 (new): Rust-native trunk SIP, no SBC shield.
- ADR-0004 (new): GPL-3.0-or-later license.
- ADR-0005 (new): Valkey as event bus + state store.
- ADR-0006 (new): WebRTC-first ingress; SIP endpoint deferred.
- vision-revision spec: status -> Ratified 2026-06-26.
- Clarify RtcSession/Channel ownership: ChannelId IS the session id;
RtcSession owns both str0m Live + codecs and the Channel. Fixes ambiguity.
- Drop the separate /ice endpoint: non-trickle ICE bundles candidates
into the SDP offer/answer, so the endpoint was redundant. Fixes
contradiction.
- Clarify idle timeout = 'no RTP for 60s' (not 5 min blanket), and that
no per-session tokio task is spawned (pre-paves the wrong pattern
for step 4's dedicated thread).
- 'reflex to police' -> 'reflex to enforce' (terminology: avoid
authoritarian verbs).
Workspace scaffold + spearhead step 1: browser talks WebRTC to the core,
core terminates DTLS-SRTP, decodes Opus to canonical 16-bit PCM @24 kHz mono,
echoes back. Proves the media core + the codec-to-PCM boundary (pre-paves
the tap). Stubs lock the ADR-0002 fused-vertical boundaries for steps
2/5/6.
Drafted in brainstorming session 2026-06-28.
Records the decisions from pressure-testing the four founding docs:
- North-star reframe: the engine for the AI-era contact center, not an
Asterisk-protocol successor (Asterisk's map = checklist, not template)
- Fused per-call vertical + composable horizontal platform (tightens the
three-plane model; removes the control<->media hot-path gRPC hop)
- Rust-native trunk SIP, IP-allowlisted; drops the Kamailio/rtpengine C
shield (reverses ADR-0001, to be formalized as ADR-0003)
- Pillars re-weighted: +data-ownership, spend-control promoted to
constitutive, WASM demoted out of the core story
- Developer-first DX spine (config-as-code, AEL lesson, engine+batteries)
- GUI as pure API client (FreePBX model) + k8s declarative/operational split
- Sequenced thin-slice first proof + capability ladder
For review before applying to README/ARCHITECTURE/PORT_PLAN and writing
ADR-0002 (identity) and ADR-0003 (SIP).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUtgQTo3wHGXP1TixHr4gd
Record the SIP edge decision and align the docs:
- docs/adr/0001-sip-strategy.md: layered strategy (own Rust parser, rent the
interop tail via a Kamailio + rtpengine SBC, grow native core behind the shield);
pjproject FFI explicitly rejected for breaking the memory-safety thesis at the
most exposed seam.
- PORT_PLAN §1 + open decisions: SIP row updated to the decided strategy.
- ARCHITECTURE: "biggest technical risk" now points at ADR-0001.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2bfD7MkqEdfnMXxXBu456
Establish rutster — a memory-safe, API-first, security-first telephony
platform; spiritual successor to Asterisk for the WebRTC/microservices era.
- README: project framing, design pillars, open decisions
- docs/ARCHITECTURE.md: three-plane (control/media/app) model
- docs/PORT_PLAN.md: every Asterisk subsystem mapped to a disposition
(core / WASM-plugin / service / edge-FFI / dropped / replaced) with rationale
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2bfD7MkqEdfnMXxXBu456