portail 2.1.0

Unified proxy/gateway: AI Gateway + MCP Gateway + CDN cache
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# yaml-language-server: $schema=https://taskfile.dev/schema.json
#
# Portail Taskfile
# https://taskfile.dev

version: '3'

vars:
  BINARY: portail
  VERSION:
    sh: cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].version'

tasks:
  default:
    desc: Show available tasks
    cmds:
      - task --list

  build:
    desc: Build debug binary
    cmds:
      - cargo build

  build-release:
    desc: Build release binary with LTO
    cmds:
      - cargo build --release

  test:
    desc: Run all tests
    cmds:
      - cargo test

  test-fast:
    desc: Run tests with nextest
    cmds:
      - cargo nextest run

  lint:
    desc: Run clippy and format check
    cmds:
      - cargo clippy --locked --all-targets -- -D warnings
      - cargo fmt --check

  format:
    desc: Format code
    cmds:
      - cargo fmt

  check:
    desc: Run cargo check (default allocator)
    cmds:
      - cargo check --locked

  check-all-allocs:
    desc: Check all allocator variants compile
    cmds:
      - cargo check 2>&1
      - cargo check --features jemalloc 2>&1
      - cargo check --features portail_system_alloc 2>&1

  bench:
    desc: Run benchmarks
    cmds:
      - cargo bench

  clean:
    desc: Clean build artifacts
    cmds:
      - cargo clean

  clean-all:
    desc: Deep clean (build artifacts + Nix store garbage)
    cmds:
      - cargo clean
      - nix-collect-garbage -d 2>/dev/null || echo "nix-collect-garbage not available"
      - rm -rf .sccache
      - echo "All build artifacts removed"

  clean-nix:
    desc: Clean Nix store garbage (keeps last 3 generations)
    cmds:
      - nix-collect-garbage --delete-older-than 7d 2>/dev/null || echo "nix not available"

  # ── Amber ──────────────────────────────────────────────────────

  amberify:
    desc: Convert shell scripts to Amber
    vars:
      INPUT: '{{.INPUT | default "scripts/"}}'
      OUTPUT: '{{.OUTPUT | default "scripts/amber/"}}'
    cmds:
      - mkdir -p {{.OUTPUT}}
      - cargo run -- amberify --input {{.INPUT}} --output {{.OUTPUT}}

  amberify-file:
    desc: Convert a single shell script to Amber
    vars:
      INPUT: '{{.INPUT | required}}'
    cmds:
      - cargo run -- amberify --input {{.INPUT}}

  # ── CI ─────────────────────────────────────────────────────────

  ci:
    desc: Run full CI pipeline
    cmds:
      - task: check
      - task: lint
      - task: test

  complexity:
    desc: Run complexity analysis
    cmds:
      - ./scripts/complexity-analysis.sh

  spec:
    desc: Verify route spec against golden file
    cmds:
      - cargo run -- spec-verify diff

  drift:
    desc: Capture/replay drift smoke test
    cmds:
      - cargo run -- drift-detect capture

  audit:
    desc: Security audit dependencies
    cmds:
      - cargo audit

  features:
    desc: Test with all feature flags
    cmds:
      - cargo test --features store-turso

  turso:
    desc: Test Turso backend
    cmds:
      - cargo test --features store-turso -- store

  # ── Docker ─────────────────────────────────────────────────────

  docker-build:
    desc: Build Docker image
    cmds:
      - docker build -t {{.BINARY}}:latest .

  docker-run:
    desc: Run Docker container
    deps: [docker-build]
    cmds:
      - docker run --rm -p 8787:8787 {{.BINARY}}:latest

  docker-slim:
    desc: Check compressed binary size
    cmds:
      - docker build -t {{.BINARY}}:slim --target build -f Dockerfile .
      - docker run --rm {{.BINARY}}:slim ls -lh /build/target/release/{{.BINARY}}

  # ── Nix ────────────────────────────────────────────────────────

  nix-check:
    desc: Run nix flake check
    cmds:
      - nix flake check --impure

  nix-build:
    desc: Build nix package
    cmds:
      - nix build .#portail

  # ── PIT (Process Interception Tracker) ──────────────────────────

  pit:
    desc: Run PIT process watcher (background daemon)
    cmds:
      - cargo run -- pit

  pit-scan:
    desc: One-shot /proc scan and PIT log
    cmds:
      - cargo run -- pit --scan

  pit-log:
    desc: Tail the PIT log file
    cmds:
      - tail -f ~/.local/share/portail/pit.log 2>/dev/null || echo "No PIT log yet"

  # ── Mermaid Render (CI/doc pipeline) ────────────────────────────

  mermaid-render:
    desc: Render mermaid diagrams in markdown to SVG (installs merman-cli)
    cmds:
      - |
        if ! command -v merman-cli &> /dev/null; then
          cargo install merman-cli
        fi
        merman-cli render --markdown {{.FILE | default "README.md"}}

  mermaid-render-all:
    desc: Render mermaid in all markdown files under docs/
    cmds:
      - |
        if ! command -v merman-cli &> /dev/null; then
          cargo install merman-cli
        fi
        find docs/ -name '*.md' -exec merman-cli render --markdown {} \;

  # ── Release ────────────────────────────────────────────────────

  release:
    desc: Build release binary with UPX
    cmds:
      - cargo build --release
      - upx --best --lzma target/release/{{.BINARY}}
      - sha256sum target/release/{{.BINARY}} | tee target/release/{{.BINARY}}.sha256

  release-sign:
    desc: Sign release with cosign
    deps: [release]
    cmds:
      - cosign sign-blob --yes --output-signature target/release/{{.BINARY}}.sig --output-certificate target/release/{{.BINARY}}.pem target/release/{{.BINARY}}
      - cosign sign-blob --yes --output-signature target/release/{{.BINARY}}.sha256.sig --output-certificate target/release/{{.BINARY}}.sha256.pem target/release/{{.BINARY}}.sha256

  release-verify:
    desc: Verify release signature
    cmds:
      - cosign verify-blob --cert target/release/{{.BINARY}}.pem --signature target/release/{{.BINARY}}.sig target/release/{{.BINARY}}

  # ── Credentials ────────────────────────────────────────────────

  login:
    desc: Login to crates.io
    cmds:
      - cargo login

  publish-dry:
    desc: Dry run publish
    cmds:
      - cargo publish --dry-run

  publish:
    desc: Publish to crates.io
    cmds:
      - cargo publish

  # ── Docs ───────────────────────────────────────────────────────

  docs:
    desc: Generate and open documentation
    cmds:
      - cargo doc --no-deps --document-private-items
      - open target/doc/portail/index.html

  # ── Chore CI Agent ─────────────────────────────────────────────

  chore-check:
    desc: Check for auto-fixable issues (advisory, never blocks)
    cmds:
      - bash scripts/rust-chore.sh check

  chore-fix:
    desc: Auto-fix imports, warnings, formatting
    cmds:
      - bash scripts/rust-chore.sh fix

  chore-verify:
    desc: Verify cargo check + test after fixes
    cmds:
      - bash scripts/rust-chore.sh verify

  chore-report:
    desc: Generate chore report for CI
    cmds:
      - bash scripts/rust-chore.sh report

  # ── Quick Dev Commands ─────────────────────────────────────────

  serve:
    desc: Start the server (debug)
    cmds:
      - cargo run -- serve

  init:
    desc: Generate portail.toml interactively
    cmds:
      - cargo run -- init

  c:
    desc: cargo check (fast feedback)
    cmds:
      - cargo check

  t:
    desc: cargo test (all suites)
    cmds:
      - cargo test

  w:
    desc: cargo watch — auto-rebuild on changes
    cmds:
      - cargo watch -x check -x test

  counts:
    desc: Show test and warning counts
    cmds:
      - echo "Tests:" && cargo test 2>&1 | tail -1
      - echo "Warnings:" && cargo check --lib --tests 2>&1 | grep -c "warning:" || echo 0

  e2e:
    desc: Run end-to-end test suite
    cmds:
      - bash scripts/e2e-test.sh

  ramdisk:
    desc: Mount RAM disk at target/ for instant compilation
    cmds:
      - |
        if [ "$(uname)" = "Linux" ]; then
          sudo mkdir -p target && sudo mount -t tmpfs -o size=8G tmpfs ./target
        elif [ "$(uname)" = "Darwin" ]; then
          diskutil erasevolume HFS+ 'RustRAM' $(hdiutil attach -nomount ram://16777216) 2>/dev/null || true
          ln -sf /Volumes/RustRAM target
        fi
        echo "RAM disk ready at ./target"

  test-fast:
    desc: Run tests in parallel (nextest)
    cmds:
      - cargo nextest run

  build-cranelift:
    desc: Build with Cranelift backend (30%+ faster)
    cmds:
      - cargo +nightly build -Zcodegen-backend=cranelift

  # ── Modern CLI (no legacy tools) ──────────────────────────────

  search:
    desc: Search src/ with ripgrep (replaces grep)
    vars:
      PATTERN: '{{.PATTERN | required}}'
    cmds:
      - rg {{.PATTERN}} src/

  tree:
    desc: Directory tree with eza (replaces ls/tree)
    cmds:
      - eza --tree --git-ignore --icons

  du:
    desc: Disk usage with dua (replaces du -sh)
    cmds:
      - dua interactive

  top:
    desc: System monitor with btm (replaces top)
    cmds:
      - btm

  json:
    desc: JSON formatting with jq
    vars:
      FILE: '{{.FILE | default "Cargo.toml"}}'
    cmds:
      - jq '.' < {{.FILE}}

  diff:
    desc: Syntax-highlighted diff with delta (replaces diff)
    cmds:
      - delta {{.CLI_ARGS}}

  ws:
    desc: WebSocket test with websocat (replaces netcat)
    vars:
      URL: '{{.URL | default "ws://localhost:8787/a2a/ws"}}'
    cmds:
      - websocat {{.URL}}

  bat:
    desc: Read file with bat (replaces cat)
    vars:
      FILE: '{{.FILE | required}}'
    cmds:
      - bat {{.FILE}}

  # ── Help ───────────────────────────────────────────────────────

  help:
    desc: Show this help
    cmds:
      - task --list

  # ── Contributor DX ───────────────────────────────────────────

  setup:
    desc: One-command full environment bootstrap for new contributors
    cmds:
      - bash scripts/contributor-setup.sh

  coverage:
    desc: Generate code coverage report (HTML) with cargo-llvm-cov
    cmds:
      - |
        if ! command -v cargo-llvm-cov &> /dev/null; then
          echo "Installing cargo-llvm-cov..."
          cargo install cargo-llvm-cov
        fi
        cargo llvm-cov --open

  coverage-report:
    desc: Generate coverage report (terminal, no open)
    cmds:
      - |
        if ! command -v cargo-llvm-cov &> /dev/null; then
          echo "Installing cargo-llvm-cov..."
          cargo install cargo-llvm-cov
        fi
        cargo llvm-cov

  profile:
    desc: Profile hot functions with samply (or dhat as fallback)
    cmds:
      - |
        if command -v samply &> /dev/null; then
          echo "Profile with samply..."
          samply record cargo run -- serve
        elif cargo install --list 2>/dev/null | grep -q 'dhat'; then
          echo "Profile with dhat (heap only)..."
          cargo run --features dhat-heap -- serve
        else
          echo "Install samply: cargo install samply"
          echo "Or use dhat: cargo install dhat"
        fi

  docs-serve:
    desc: Build docs and serve locally with mdbook or python http.server
    cmds:
      - |
        if command -v mdbook &> /dev/null; then
          if [ -f book.toml ]; then
            mdbook serve --open
          else
            echo "No book.toml found, using cargo doc"
            task: docs
          fi
        else
          task: docs
        fi

  deny:
    desc: Run cargo-deny license and duplicate check
    cmds:
      - cargo deny check

  audit:
    desc: Run cargo-audit security advisory check
    cmds:
      - cargo audit

  # ── Pre-commit Hook ──────────────────────────────────────────

  install-hooks:
    desc: Install git pre-commit hooks
    cmds:
      - |
        HOOKS_DIR=".git/hooks"
        HOOK_FILE="$HOOKS_DIR/pre-commit"
        if [ -f "$HOOK_FILE" ]; then
          echo "Pre-commit hook already exists, overwrite? [y/N]"
          read -r resp
          [ "$resp" != "y" ] && exit 0
        fi
        cat > "$HOOK_FILE" << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
echo "=== Pre-commit: cargo fmt --check ==="
cargo fmt --check || { echo "❌ Formatting issues — run 'cargo fmt' to fix"; exit 1; }
echo "=== Pre-commit: cargo clippy ==="
cargo clippy --locked --all-targets -- -D warnings || exit 1
echo "=== Pre-commit: cargo check ==="
cargo check --locked || exit 1
echo "✅ Pre-commit checks passed"
EOF
        chmod +x "$HOOK_FILE"
        echo "✅ Pre-commit hook installed at $HOOK_FILE"

  # ── Not-in-Plan Dev Tasks ────────────────────────────────────

  outdated:
    desc: Check for outdated dependencies
    cmds:
      - cargo outdated

  tree:
    desc: Display dependency tree
    cmds:
      - cargo tree

  udeps:
    desc: Find unused dependencies
    cmds:
      - |
        if ! command -v cargo-udeps &> /dev/null; then
          cargo install cargo-udeps
        fi
        cargo +nightly udeps