slancha-wire 0.15.0

Magic-wormhole for AI agents — bilateral signed-message bus over a mailbox relay
Documentation
name: ci

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo build --all-targets
      # Force single-threaded test execution to keep the heavy real-process
      # e2e binaries (e.g. e2e_detached_pair, with real daemons + a SAS
      # handshake under a tight deadline) from self-contending under default
      # parallelism on the 2-core ubuntu-latest runner. Also reduces the UDS
      # round-trip flake seen intermittently on Broken-pipe (os error 32).
      # Lib + non-heavy e2e cost is negligible serialized; heavy e2e is where
      # the wins are. See feedback/heavy-e2e-subprocess-contention.
      - run: cargo test --all-targets -- --test-threads=1

  fmt:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - run: cargo fmt --all -- --check

  clippy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
      - run: cargo clippy --all-targets -- -D warnings

  demo-invite:
    # Runs demo-invite.sh end-to-end: local relay + paul + willard + one-paste
    # invite-URL pair + bidirectional signed send/recv. Catches regressions in
    # the v0.4.0 pair_drop hook, daemon-pull cursor persist, and CLI wiring.
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: sudo apt-get update && sudo apt-get install -y jq
      - run: cargo build --release --bin wire
      - run: WIRE=./target/release/wire bash demo-invite.sh

  demo-hotline:
    # Runs demo-hotline.sh end-to-end: local relay + 5 agents with vibes +
    # full mesh via `wire add` + signed ring-send. Validates the v0.5
    # handle directory, .well-known/wire/agent resolver, pair_drop +
    # pair_drop_ack flow, profile fields, and 5-way mesh under one daemon.
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: sudo apt-get update && sudo apt-get install -y jq
      - run: cargo build --release --bin wire
      - run: WIRE=./target/release/wire bash demo-hotline.sh

  docs-lint:
    # Catch deprecated command phrases drifting back into docs. Per the
    # swift-harbor / dthoma1 #145 audit: README + AGENTS + INSTALL had
    # several stale shapes (`wire add bob@`, `wire init <nick>`, `wire
    # daemon start`, `wire.slancha.ai`, `wire up <nick>@<relay>`) that
    # contradicted the canonical v0.12+ surface. Lint blocks regression.
    #
    # Legacy sections explicitly named "Legacy" or "Legacy flows" are
    # allowed to mention deprecated verbs — the lint scopes its grep
    # exclusions to those headers (handled inline via `grep -v`).
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: deprecated phrases (canonical surface drift)
        run: |
          set -e
          FILES="README.md AGENTS.md INSTALL.md docs/integrations/*.md docs/AGENT_INTEGRATION.md"
          fail=0

          check() {
            local pattern="$1"
            local description="$2"
            # Exclude lines inside Legacy sections (best-effort: grep -v on
            # the line itself contains 'Legacy' marker; deeper context
            # exclusion would need a real parser).
            hits=$(grep -nE "$pattern" $FILES 2>/dev/null \
              | grep -v -iE '(legacy|deprecated|removed|v1\.0 removes)' \
              || true)
            if [ -n "$hits" ]; then
              echo "::error::Deprecated phrase '$description' found in docs:"
              echo "$hits"
              fail=1
            fi
          }

          # The shapes swift-harbor's audit (#145) called out:
          check 'wire add [a-z][a-z-]*@' 'wire add <peer>@<relay> (use `wire dial`)'
          check 'wire init <nick>' 'wire init <nick> (use `wire up`)'
          check 'wire daemon start' 'wire daemon start (no subcommand; use `wire daemon`)'
          check 'wire up <nick>@' 'wire up <nick>@<relay> (handle is DID-derived; use `wire up @<relay>`)'
          check 'wire\.slancha\.ai' 'wire.slancha.ai (use wireup.net)'
          check 'relay\.slancha\.ai' 'relay.slancha.ai (use wireup.net)'

          if [ "$fail" -ne 0 ]; then
            echo ""
            echo "Lint failed: deprecated command phrase(s) drifted back into docs."
            echo "Fix the lines above or move them under a 'Legacy' header if intentional."
            exit 1
          fi
          echo "docs-lint: clean."

  install-smoke:
    # Fresh-user first-run path. Builds the binary THIS PR produces, puts it
    # on PATH the way an install leaves it, and — from a clean cwd with an
    # empty WIRE_HOME — runs the offline out-of-the-box sequence. Catches
    # changes that compile + pass tests but break the new-user experience
    # (first-run offline identity, --json shape, basic subcommands). Also
    # This is the "test-env in CI" smoke; the demos cover deeper e2e flows.
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: sudo apt-get update && sudo apt-get install -y jq
      - run: cargo build --release --bin wire
      - name: fresh-user smoke (offline, clean WIRE_HOME)
        run: |
          set -euo pipefail
          sudo install -m 0755 target/release/wire /usr/local/bin/wire
          export WIRE_HOME="$(mktemp -d)/home" WIRE_QUIET_AUTOSESSION=1
          cd "$(mktemp -d)"   # clean cwd — no repo, no existing state
          wire --version
          wire --help >/dev/null
          wire whoami --json | jq -e '.initialized == false'    # pre-init
          # `wire up --no-local` creates the local identity but also tries to
          # claim its DID-derived handle on the shared relay — that claim can
          # 409 if the random persona is already taken (non-fatal). The real
          # check is the local identity, so tolerate a nonzero exit and assert
          # via whoami.
          wire up --no-local || true
          wire whoami --json | jq -e '.did | startswith("did:wire:")'
          wire here >/dev/null
          echo "install-smoke: fresh-user offline path OK"
      - name: nuke smoke (dry-run changes nothing; --force resets; re-up works)
        run: |
          set -euo pipefail
          export WIRE_HOME="$(mktemp -d)/home" WIRE_QUIET_AUTOSESSION=1
          wire up --no-local || true
          wire whoami --json | jq -e '.did | startswith("did:wire:")'   # identity created
          wire nuke --dry-run                              # lists, must change nothing
          wire whoami --json | jq -e '.did | startswith("did:wire:")'   # dry-run kept it
          wire nuke --force --json | jq -e 'has("removed_paths")'
          wire whoami --json | jq -e '.initialized == false'            # state wiped
          wire up --no-local || true                       # fresh again
          wire whoami --json | jq -e '.did | startswith("did:wire:")'
          echo "install-smoke: nuke + re-up OK"

  install-smoke-windows:
    # Windows leg of the cross-platform gate: build wire.exe and run the
    # fresh-user + nuke smoke in PowerShell from a clean WIRE_HOME. Windows
    # can't be exercised on the maintainer's mac/linux hosts, so this CI
    # job is the primary Windows signal (plus an operator confirm on a real
    # box before merge).
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo build --release --bin wire
      - name: fresh-user + nuke smoke (PowerShell)
        shell: pwsh
        run: |
          $ErrorActionPreference = "Stop"
          $PSNativeCommandUseErrorActionPreference = $true   # native non-zero exit → throw
          $wire = ".\target\release\wire.exe"
          $env:WIRE_HOME = Join-Path $env:RUNNER_TEMP ("wh-" + [guid]::NewGuid().ToString("N"))
          $env:WIRE_QUIET_AUTOSESSION = "1"
          & $wire --version
          & $wire --help | Out-Null
          $pre = (& $wire whoami --json | ConvertFrom-Json)
          if ($pre.initialized -ne $false) { throw "expected initialized:false pre-init" }
          # `wire up --no-local` creates the local identity but also tries to
          # claim its handle on the shared relay; that claim can 409 (handle
          # already taken) which is non-fatal. Tolerate a nonzero exit and
          # assert the real outcome (local DID) via whoami.
          try { & $wire up --no-local } catch { Write-Host "wire up claim non-fatal: $_" }
          $post = (& $wire whoami --json | ConvertFrom-Json)
          if (-not $post.did.StartsWith("did:wire:")) { throw "expected a did:wire: DID" }
          & $wire here | Out-Null
          & $wire nuke --dry-run
          $afterDry = (& $wire whoami --json | ConvertFrom-Json)
          if (-not $afterDry.did.StartsWith("did:wire:")) { throw "dry-run must not wipe state" }
          & $wire nuke --force --json | ConvertFrom-Json | Out-Null
          $afterNuke = (& $wire whoami --json | ConvertFrom-Json)
          if ($afterNuke.initialized -ne $false) { throw "nuke --force should wipe state" }
          try { & $wire up --no-local } catch { Write-Host "wire up claim non-fatal: $_" }
          $fresh = (& $wire whoami --json | ConvertFrom-Json)
          if (-not $fresh.did.StartsWith("did:wire:")) { throw "re-up should recreate identity" }
          Write-Host "install-smoke-windows: fresh-user + nuke OK"