-
Notifications
You must be signed in to change notification settings - Fork 0
225 lines (224 loc) · 13.2 KB
/
Copy pathci.yml
File metadata and controls
225 lines (224 loc) · 13.2 KB
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
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
build-and-test:
strategy:
# One platform's failure says nothing about the other's; let both finish.
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-wasip1
- uses: actions/cache@v4
with:
# ~/.almide holds the build cache for test files that take Almide's
# native path; without it every run rebuilds them from scratch.
path: |
~/.cargo
~/.almide
target
# The cached path list is not part of the key, so adding a path to an
# existing key would restore the old entry and never save the new one.
# Bump this token whenever the paths change.
key: ${{ runner.os }}-porta-v2-${{ hashFiles('almide.toml', '.almide-version') }}
restore-keys: ${{ runner.os }}-porta-v2-
- name: Install pinned Almide
run: bash scripts/install-almide.sh
- name: Install pinned Wasmtime
# Almide runs a test file through wasmtime when it is on PATH and
# otherwise builds it natively, which dominated the job time. This is an
# accelerator, not a dependency: a failed download must leave the job
# slow rather than red, so the tests still decide whether CI passes.
continue-on-error: true
run: |
bash scripts/install-wasmtime.sh
echo "$PWD/.tools/wasmtime" >> "$GITHUB_PATH"
- name: Code quality grade
# The score depends on the analyzer's own thresholds and on how much of
# this repository's Almide its grammar can parse, so the version is
# pinned and the analysis runs on one platform. The floor is the grade-A
# boundary: this gate exists to catch the repository dropping below it,
# and moving the floor is a deliberate edit, not a passing fix.
if: matrix.os == 'ubuntu-latest'
run: |
bash scripts/install-codopsy.sh
.tools/codopsy/codopsy analyze . -o "$RUNNER_TEMP/codopsy.json"
python3 scripts/check_grade.py "$RUNNER_TEMP/codopsy.json" --min-score 90
- name: Evaluation grader tests
run: |
python3 -m unittest discover -s scripts/evals -p 'test_*.py'
python3 scripts/audit_eval.py docs/benchmarks/2026-09-19-real-model.json.gz
python3 scripts/audit_eval.py docs/benchmarks/2026-09-19-verification-fixed.json.gz
python3 scripts/audit_eval.py docs/benchmarks/2026-09-19-compact-results.json.gz
python3 scripts/audit_eval.py docs/benchmarks/2026-09-19-pre-tool-policy.json.gz
python3 scripts/audit_eval.py docs/benchmarks/2026-09-20-loop-notice.json.gz
python3 scripts/audit_eval.py docs/benchmarks/2026-09-20-loop-control.json.gz
python3 scripts/audit_eval.py docs/benchmarks/2026-09-20-shared-compute.json.gz
python3 scripts/audit_containment.py docs/benchmarks/2026-09-20-containment.json.gz
python3 scripts/audit_containment.py docs/benchmarks/2026-09-20-containment-fixed.json.gz
python3 scripts/audit_compute_smoke.py docs/benchmarks/2026-09-20-compute-smoke.json.gz
- name: Audit saved startup and memory measurements
run: |
python3 scripts/audit_benchmark.py docs/benchmarks/2026-09-20-startup.json.gz
python3 scripts/audit_benchmark.py docs/benchmarks/2026-09-20-startup-recorded.json.gz --porta-record
- name: Check
run: .tools/almide/almide check src/main.almd
- name: Build
run: .tools/almide/almide build src/main.almd -o target/porta
# TLS is rustls only (reqwest without its default features), so the Linux
# binary needs no libssl.so.3 from whatever image it lands in (#35). A
# dependency that turns native-tls back on shows up here.
- name: No OpenSSL linked
if: runner.os == 'Linux'
run: |
ldd target/porta
! ldd target/porta | grep -E 'libssl|libcrypto'
- name: Test
run: .tools/almide/almide test --ci
# The memory ceiling is a cgroup v2 scope asked of the systemd user
# manager, and a runner job has no login session, so no user manager
# runs for it. Lingering starts one, and the suite then proves the
# ceiling on Linux (an OOM kill at 64 MiB) instead of only its refusal.
- name: Start a systemd user manager for the memory ceiling
if: matrix.os == 'ubuntu-latest'
run: |
sudo loginctl enable-linger "$USER"
for _ in $(seq 1 20); do [ -S "/run/user/$(id -u)/bus" ] && break; sleep 1; done
ls -la "/run/user/$(id -u)/bus"
echo "XDG_RUNTIME_DIR=/run/user/$(id -u)" >> "$GITHUB_ENV"
echo "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus" >> "$GITHUB_ENV"
# --why traces the run with strace on Linux.
- name: Install strace for --why
if: matrix.os == 'ubuntu-latest'
run: command -v strace || (sudo apt-get update -qq && sudo apt-get install -y -qq strace)
- name: Integration
run: python3 scripts/integration.py target/porta
- name: Escape corpus
run: python3 scripts/escapes.py target/porta
# The runner restricts unprivileged user namespaces through AppArmor, as
# Ubuntu does from 23.10, so the two steps above prove the run without
# them. The profile Ubuntu documents for such a program lets porta have
# them, and the same two suites then prove the namespaces on x86_64.
# The .deb's postinst loads the AppArmor profile for /usr/bin/porta on a
# runner that restricts user namespaces, as this one does; removing the
# package must take the profile with it.
- name: The .deb gives porta its namespaces and removes cleanly
if: matrix.os == 'ubuntu-latest'
run: |
cp target/porta "$RUNNER_TEMP/porta" && strip "$RUNNER_TEMP/porta"
bash scripts/build-deb.sh "$RUNNER_TEMP/porta" 0.0.0 amd64 "$RUNNER_TEMP/deb"
sudo apt-get install -y "$RUNNER_TEMP/deb/porta_0.0.0_amd64.deb"
/usr/bin/porta check | grep -E '^ok +own PID, mount and network namespace'
sudo apt-get remove -y porta
test ! -e /etc/apparmor.d/usr.bin.porta
- name: Integration and escape corpus with user namespaces
if: matrix.os == 'ubuntu-latest'
run: |
# The build sits in the checkout, which the runner's user can write;
# the helper script refuses it, and porta setup makes a root-owned
# copy with the profile for that copy alone.
! sudo bash scripts/apparmor-userns.sh target/porta
# The runner's /usr/local/bin is not root's alone, so setup puts the
# copy in /usr/libexec/porta and links /usr/local/bin/porta to it.
sudo target/porta setup
set_up=$(readlink -f /usr/local/bin/porta)
"$set_up" check | grep -E '^ok +own PID, mount and network namespace'
python3 scripts/integration.py "$set_up"
python3 scripts/escapes.py "$set_up"
sudo "$set_up" setup --undo
! ls /etc/apparmor.d/*.porta 2>/dev/null
# The seeds that found each fixed bug, so the fix stays fixed, then one
# fresh seed per push, printed, so a new find can be replayed.
- name: Fuzz
run: |
python3 scripts/fuzz.py target/porta policy --iterations 250 --seed 2
python3 scripts/fuzz.py target/porta proxy --iterations 4 --seed 7
python3 scripts/fuzz.py target/porta cli --iterations 300 --seed 1
python3 scripts/fuzz.py target/porta policy --iterations 150
python3 scripts/fuzz.py target/porta proxy --iterations 4
# Operations strung together at random: runs with random grants and
# misbehaviour, snapshots and rollbacks, explain, init and up, runs
# killed part way. Seed 13 found two snapshots in one second rolled
# back to the wrong one; it stays, beside one fresh seed per push.
- name: Monkey
run: |
python3 scripts/monkey.py target/porta --steps 150 --seed 13
python3 scripts/monkey.py target/porta --steps 100
# Not a gate: the numbers move with the runner. Printed so every push
# leaves what porta cost on that commit in its log.
- name: Overhead
run: python3 scripts/overhead.py target/porta --runs 20
- name: Build WASM agents and probes
run: |
.tools/almide/almide build examples/chat-agent/src/mod.almd --target wasm -o examples/chat-agent/agent.wasm
.tools/almide/almide build examples/demo-agent/src/mod.almd --target wasm -o examples/demo-agent/agent.wasm
.tools/almide/almide build scripts/fixtures/guest_isolation.almd --target wasm -o target/guest-isolation.wasm
.tools/almide/almide build scripts/fixtures/verify_readonly.almd --target wasm -o target/verify-readonly.wasm
cargo test --locked --manifest-path examples/verify-json/Cargo.toml --target-dir target/verify-json
cargo build --locked --manifest-path examples/verify-json/Cargo.toml --target wasm32-wasip1 --release --target-dir target/verify-json
cp target/verify-json/wasm32-wasip1/release/porta-verify-json.wasm examples/verify-json/check.wasm
cp target/verify-json/wasm32-wasip1/release/verify-tool.wasm examples/verify-json/verify-tool.wasm
cp target/verify-json/wasm32-wasip1/release/before-tool.wasm examples/verify-json/before-tool.wasm
cargo test --locked --manifest-path examples/compute/Cargo.toml --target-dir target/compute
cargo build --locked --manifest-path examples/compute/Cargo.toml --target wasm32-wasip1 --release --target-dir target/compute
cp target/compute/wasm32-wasip1/release/porta-compute.wasm examples/compute/compute.wasm
- name: WASM computation and exact JSON transport
run: python3 scripts/compute_integration.py target/porta examples/chat-agent/agent.wasm examples/compute/compute.wasm examples/demo-agent/agent.wasm
- name: WASM agents and teams
run: python3 scripts/agent_integration.py target/porta examples/chat-agent/agent.wasm examples/demo-agent/agent.wasm target/guest-isolation.wasm
- name: Durable resume and replay
run: python3 scripts/journal_integration.py target/porta examples/chat-agent/agent.wasm examples/demo-agent/agent.wasm
- name: Tool argument validation
run: python3 scripts/schema_integration.py target/porta examples/chat-agent/agent.wasm examples/demo-agent/agent.wasm
- name: MCP agent security and recovery
run: python3 scripts/mcp_agent_integration.py target/porta examples/chat-agent/agent.wasm examples/verify-json/verify-tool.wasm
- name: Official MCP SDK interoperability
run: |
python3 -m venv "$RUNNER_TEMP/porta-mcp-sdk"
"$RUNNER_TEMP/porta-mcp-sdk/bin/python" -m pip install mcp==1.30.0
"$RUNNER_TEMP/porta-mcp-sdk/bin/python" scripts/mcp_sdk_integration.py target/porta examples/chat-agent/agent.wasm
"$RUNNER_TEMP/porta-mcp-sdk/bin/python" scripts/compute_mcp_integration.py target/porta examples/compute/compute.wasm examples/demo-agent/agent.wasm
- name: Verified completion and recovery
run: python3 scripts/verification_integration.py target/porta examples/chat-agent/agent.wasm examples/demo-agent/agent.wasm examples/verify-json/check.wasm target/verify-readonly.wasm
- name: Pre-tool policies and recovery
run: python3 scripts/before_tool_integration.py target/porta examples/chat-agent/agent.wasm examples/demo-agent/agent.wasm examples/verify-json/before-tool.wasm examples/verify-json/check.wasm
- name: Model batch repair and recovery
run: python3 scripts/batch_recovery_integration.py target/porta examples/chat-agent/agent.wasm examples/demo-agent/agent.wasm
- name: Artifact pins and strict team loading
run: python3 scripts/artifact_pins_integration.py target/porta examples/chat-agent/agent.wasm examples/demo-agent/agent.wasm
- name: Offline team configuration inspection
run: python3 scripts/agent_check_integration.py target/porta examples/chat-agent/agent.wasm examples/demo-agent/agent.wasm
# The action a workflow uses to confine its own steps: install a released
# porta against its signature, and on Ubuntu give it the namespaces the
# runner's AppArmor otherwise withholds. Tried as a user would use it.
action:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- id: porta
uses: ./
with:
version: v0.6.11
why: "true"
- name: Confine a step
run: |
porta run /bin/sh -v . -- -c 'echo inside > granted.txt && cat granted.txt'
! porta run /bin/sh -v . -- -c "echo outside > \"$HOME/porta-action-escape\""
test ! -e "$HOME/porta-action-escape"
- name: The namespaces are there
if: runner.os == 'Linux'
run: porta check | grep -E '^ok +own PID, mount and network namespace'