Hub API report
All API tests pass
Requests
2771/2771
Bruno requests passing
Checks
8424/8424
assertions and tests
Spec coverage
638/691
operations the tests call (92%)
p95, median route
0.95 ms
123 GET routes × 20
Slowest p95
32.9 ms
/v1/overview
Fuzzing findings
0
none
API tests (Bruno)
The collection in api-tests/bruno, run against a scratch hub: no database, no node daemon, no git host. A failing request fails the pull request.
hub
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Read the hub's identity | GET /v1/hub/identity | 200 | 10.0 | 99 B | pass |
| Read the hub's overview | GET /v1/overview | 200 | 1.00 | 202 B | pass |
| Answer 404 for an unknown endpoint | GET /v1/no-such-endpoint | 404 | 3.00 | 28 B | pass |
| Describe the hub's identity | GET /v1/hub/identity | 200 | 1.00 | 99 B | pass |
| Answer 502 for the hub status while its daemon is down | GET /v1/hub | 502 | 1.00 | 101 B | pass |
| Refuse a plain HTTP GET to the event socket: it is not an upgrade | GET /v1/ws | 400 | 1.00 | 79 B | pass |
| Refuse an event socket upgrade that speaks another WebSocket version (426) | GET /v1/ws | 426 | 2.00 | 44 B | pass |
| Refuse an event socket upgrade with no key | GET /v1/ws | 400 | 2.00 | 40 B | pass |
| Refuse an event socket upgrade whose key is not 16 bytes | GET /v1/ws | 400 | 3.00 | 55 B | pass |
| Refuse an event socket opened from another origin (403) | GET /v1/ws | 403 | 3.00 | 57 B | pass |
| Refuse an event socket that does not offer the event protocol | GET /v1/ws | 400 | 2.00 | 65 B | pass |
skills
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the skill rules and the template | GET /v1/skills/rules | 200 | 2.00 | 2.3 KiB | pass |
| Check a clean skill: no errors | POST /v1/skills/check | 200 | 2.00 | 281 B | pass |
| Check a skill with no description and a dead link | POST /v1/skills/check | 200 | 1.00 | 454 B | pass |
| Check a SKILL.md with no frontmatter | POST /v1/skills/check | 200 | 1.00 | 303 B | pass |
| Refuse a check whose file has no size | POST /v1/skills/check | 400 | 2.00 | 53 B | pass |
| Refuse a check with a body that is not JSON | POST /v1/skills/check | 400 | 2.00 | 53 B | pass |
| The built-in skill is live and bound to the whole plaza | GET /v1/skills/show-your-work | 200 | 2.00 | 7.0 KiB | pass |
| Refuse an import preview that is not a zip | POST /v1/skills/import/preview | 422 | 1.00 | 63 B | pass |
| Refuse an import preview with no body | POST /v1/skills/import/preview | 422 | 3.00 | 63 B | pass |
| Answer 404 when committing an unknown preview | POST /v1/skills/import/commit | 404 | 2.00 | 46 B | pass |
| Refuse an import commit with a body that is not JSON | POST /v1/skills/import/commit | 400 | 2.00 | 53 B | pass |
| Refuse a GitHub preview for a URL that is not GitHub | POST /v1/skills/github/preview | 422 | 2.00 | 50 B | pass |
| Refuse a GitHub preview whose body has no url | POST /v1/skills/github/preview | 400 | 1.00 | 46 B | pass |
| Answer 502 for a GitHub preview the scratch hub cannot reach | POST /v1/skills/github/preview | 502 | 255 | 56 B | pass |
| Refuse an export of several skills with no ids | GET /v1/skills/export | 422 | 2.00 | 24 B | pass |
| Answer 404 when exporting an unknown skill | GET /v1/skills/export?ids=no-such-skill | 404 | 2.00 | 25 B | pass |
| Create a skill with a reference file | POST /v1/skills | 201 | 133 | 416 B | pass |
| Refuse a second skill with the same id | POST /v1/skills | 409 | 71.0 | 55 B | pass |
| Refuse a second live skill with the same frontmatter name | POST /v1/skills | 409 | 20.0 | 53 B | pass |
| Refuse a skill id with capitals and underscores | POST /v1/skills | 422 | 2.00 | 98 B | pass |
| Refuse a file path that climbs out of the skill | POST /v1/skills | 422 | 2.00 | 62 B | pass |
| Refuse a skill whose title is only spaces | POST /v1/skills | 422 | 2.00 | 42 B | pass |
| Refuse a title of 121 characters | POST /v1/skills | 422 | 2.00 | 42 B | pass |
| Refuse a binding that is neither plaza nor a role or project | POST /v1/skills | 422 | 9.00 | 60 B | pass |
| Refuse more than 16 tags | POST /v1/skills | 422 | 2.00 | 46 B | pass |
| Refuse a file whose content is not base64 | POST /v1/skills | 422 | 2.00 | 43 B | pass |
| Refuse a create body without SKILL.md | POST /v1/skills | 400 | 2.00 | 53 B | pass |
| Read the created skill back | GET /v1/skills/t-skill-life | 200 | 1.00 | 713 B | pass |
| Read version 1 with its file and findings | GET /v1/skills/t-skill-life/versions/1 | 200 | 1.00 | 651 B | pass |
| Download the reference file of version 1 | GET /v1/skills/t-skill-life/versions/1/files/reference.md | 200 | 1.00 | 12 B | pass |
| Answer 404 for a file the version does not hold | GET /v1/skills/t-skill-life/versions/1/files/nope.md | 404 | 1.00 | 36 B | pass |
| Answer 404 for a version that does not exist | GET /v1/skills/t-skill-life/versions/9 | 404 | 1.00 | 36 B | pass |
| Answer 404 for a version that is not a number | GET /v1/skills/t-skill-life/versions/one | 404 | 1.00 | 36 B | pass |
| Answer 404 for a version of an unknown skill | GET /v1/skills/no-such-skill/versions/1 | 404 | 1.00 | 36 B | pass |
| The history starts with the create | GET /v1/skills/t-skill-life/history | 200 | 1.00 | 222 B | pass |
| Set the bindings of a skill (sorted and without repeats) | PUT /v1/skills/t-skill-life/bindings | 200 | 40.0 | 424 B | pass |
| Refuse a binding that names no consumer | PUT /v1/skills/t-skill-life/bindings | 422 | 1.00 | 57 B | pass |
| Answer 404 when setting bindings of an unknown skill | PUT /v1/skills/no-such-skill/bindings | 404 | 19.0 | 36 B | pass |
| Rename the skill and retag it | PUT /v1/skills/t-skill-life | 200 | 12.0 | 409 B | pass |
| Refuse a rename to an empty title | PUT /v1/skills/t-skill-life | 422 | 2.00 | 42 B | pass |
| Answer 404 when renaming an unknown skill | PUT /v1/skills/no-such-skill | 404 | 13.0 | 36 B | pass |
| Add version 2 with a changed SKILL.md | POST /v1/skills/t-skill-life/versions | 201 | 34.0 | 289 B | pass |
| Send the same version again: nothing new, the file by its hash | POST /v1/skills/t-skill-life/versions | 200 | 1.00 | 290 B | pass |
| Refuse a version built on an old base (409) | POST /v1/skills/t-skill-life/versions | 409 | 22.0 | 47 B | pass |
| Refuse a version whose file path climbs out of the skill | POST /v1/skills/t-skill-life/versions | 422 | 1.00 | 58 B | pass |
| Refuse a file named SKILL.md (it is the body, not a file) | POST /v1/skills/t-skill-life/versions | 422 | 1.00 | 58 B | pass |
| Refuse a file sent by a hash the hub does not hold | POST /v1/skills/t-skill-life/versions | 422 | 3.00 | 103 B | pass |
| Refuse a file given both its bytes and its hash | POST /v1/skills/t-skill-life/versions | 422 | 2.00 | 60 B | pass |
| Refuse a version whose body is not JSON | POST /v1/skills/t-skill-life/versions | 400 | 1.00 | 53 B | pass |
| Answer 404 when adding a version to an unknown skill | POST /v1/skills/no-such-skill/versions | 404 | 1.00 | 36 B | pass |
| Add version 3 that links a file it does not hold (an error finding) | POST /v1/skills/t-skill-life/versions | 201 | 14.0 | 299 B | pass |
| Read the broken version: the finding names the missing file | GET /v1/skills/t-skill-life/versions/3 | 200 | 2.00 | 819 B | pass |
| Read version 2: no findings to recheck | GET /v1/skills/t-skill-life/versions/2 | 200 | 2.00 | 673 B | pass |
| Compare version 1 with version 2 | GET /v1/skills/t-skill-life/diff?from=1&to=2 | 200 | 1.00 | 1.9 KiB | pass |
| Compare version 2 with the broken version 3 | GET /v1/skills/t-skill-life/diff?from=2&to=3 | 200 | 1.00 | 2.1 KiB | pass |
| Refuse a compare without both version numbers | GET /v1/skills/t-skill-life/diff?from=1 | 422 | 1.00 | 47 B | pass |
| Answer 404 when comparing with a version that does not exist | GET /v1/skills/t-skill-life/diff?from=1&to=99 | 404 | 2.00 | 36 B | pass |
| Export version 2 as a zip | GET /v1/skills/t-skill-life/versions/2/export | 200 | 2.00 | 424 B | pass |
| Answer 404 when exporting a version that does not exist | GET /v1/skills/t-skill-life/versions/99/export | 404 | 1.00 | 25 B | pass |
| Answer 404 when exporting a version that is not a number | GET /v1/skills/t-skill-life/versions/x/export | 404 | 1.00 | 27 B | pass |
| The history records each version written | GET /v1/skills/t-skill-life/history | 200 | 1.00 | 638 B | pass |
| Refuse to publish a version with an error finding | POST /v1/skills/t-skill-life/publish | 422 | 2.00 | 81 B | pass |
| Refuse to publish a version that does not exist | POST /v1/skills/t-skill-life/publish | 422 | 1.00 | 42 B | pass |
| Answer 404 when publishing an unknown skill | POST /v1/skills/no-such-skill/publish | 404 | 2.00 | 36 B | pass |
| Refuse a publish whose body is not JSON | POST /v1/skills/t-skill-life/publish | 400 | 2.00 | 53 B | pass |
| Publish version 2 as the live one | POST /v1/skills/t-skill-life/publish | 200 | 18.0 | 405 B | pass |
| The live version shows what was published | GET /v1/skills/t-skill-life | 200 | 2.00 | 1.9 KiB | pass |
| The skill list shows the live version | GET /v1/skills | 200 | 2.00 | 952 B | pass |
| Unpublish the skill: nothing is live | POST /v1/skills/t-skill-life/unpublish | 200 | 28.0 | 409 B | pass |
| Unpublishing an unpublished skill changes nothing | POST /v1/skills/t-skill-life/unpublish | 200 | 12.0 | 409 B | pass |
| Answer 404 when unpublishing an unknown skill | POST /v1/skills/no-such-skill/unpublish | 404 | 8.00 | 36 B | pass |
| Publish the older version 1 again (a rollback) | POST /v1/skills/t-skill-life/publish | 200 | 9.00 | 405 B | pass |
| The history records publish and unpublish | GET /v1/skills/t-skill-life/history | 200 | 1.00 | 1.0 KiB | pass |
| Create a second skill to archive | POST /v1/skills | 201 | 10.0 | 365 B | pass |
| Publish the second skill's version 1 | POST /v1/skills/t-skill-arc/publish | 200 | 12.0 | 361 B | pass |
| Archive the published skill | POST /v1/skills/t-skill-arc/archive | 200 | 9.00 | 370 B | pass |
| Archiving an archived skill keeps its first archive time | POST /v1/skills/t-skill-arc/archive | 200 | 8.00 | 370 B | pass |
| The default list leaves an archived skill out | GET /v1/skills | 200 | 1.00 | 952 B | pass |
| The archived list shows the archived skill | GET /v1/skills?archived=true | 200 | 1.00 | 1.3 KiB | pass |
| An archived skill still reads with its versions | GET /v1/skills/t-skill-arc | 200 | 1.00 | 1.1 KiB | pass |
| Refuse to export an archived skill | GET /v1/skills/export?ids=t-skill-arc | 422 | 1.00 | 35 B | pass |
| Refuse a GitHub check on a skill made in the app | POST /v1/skills/t-skill-arc/github/check | 422 | 1.00 | 51 B | pass |
| Refuse a GitHub update on a skill made in the app | POST /v1/skills/t-skill-arc/github/update | 422 | 1.00 | 51 B | pass |
| Answer 404 for a GitHub check on an unknown skill | POST /v1/skills/no-such-skill/github/check | 404 | 2.00 | 25 B | pass |
| Restore the archived skill | POST /v1/skills/t-skill-arc/restore | 200 | 18.0 | 361 B | pass |
| The restored skill is back in the default list | GET /v1/skills | 200 | 2.00 | 1.3 KiB | pass |
| Restoring a live skill changes nothing | POST /v1/skills/t-skill-arc/restore | 200 | 16.0 | 361 B | pass |
| Answer 404 when archiving an unknown skill | POST /v1/skills/no-such-skill/archive | 404 | 12.0 | 36 B | pass |
| Answer 404 when restoring an unknown skill | POST /v1/skills/no-such-skill/restore | 404 | 11.0 | 36 B | pass |
| Export two skills as one zip | GET /v1/skills/export?ids=t-skill-life,t-skill-arc | 200 | 2.00 | 630 B | pass |
| Export version 1 of the restored skill as a zip | GET /v1/skills/t-skill-arc/versions/1/export | 200 | 2.00 | 245 B | pass |
| Export a skill named twice: it is zipped once | GET /v1/skills/export?ids=t-skill-life,t-skill-life | 200 | 2.00 | 407 B | pass |
| Answer 404 when one listed skill is unknown | GET /v1/skills/export?ids=t-skill-life,no-such-skill | 404 | 1.00 | 25 B | pass |
| Refuse an export whose ids are only commas | GET /v1/skills/export?ids=,, | 422 | 2.00 | 24 B | pass |
| Archive the test skill with its versions | POST /v1/skills/t-skill-life/archive | 200 | 11.0 | 414 B | pass |
| Archive the second test skill | POST /v1/skills/t-skill-arc/archive | 200 | 8.00 | 370 B | pass |
| Neither test skill is in the default list any more | GET /v1/skills | 200 | 1.00 | 556 B | pass |
| Both test skills are in the archived list | GET /v1/skills?archived=true | 200 | 1.00 | 1.3 KiB | pass |
| List skills on a fresh hub (only the built-in ones) | GET /v1/skills | 200 | 1.00 | 556 B | pass |
| Create a skill | POST /v1/skills | 201 | 21.0 | 358 B | pass |
| Read the skill back | GET /v1/skills/pdf-forms | 200 | 2.00 | 655 B | pass |
| Change a skill's title and tags | PUT /v1/skills/pdf-forms | 200 | 14.0 | 374 B | pass |
| List skills after the change | GET /v1/skills | 200 | 1.00 | 921 B | pass |
| Archive a skill | POST /v1/skills/pdf-forms/archive | 200 | 9.00 | 383 B | pass |
| Answer 404 for an unknown skill | GET /v1/skills/no-such-skill | 404 | 1.00 | 36 B | pass |
| Refuse a create without a title | POST /v1/skills | 400 | 1.00 | 53 B | pass |
prompts
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the prompt rules and templates | GET /v1/prompts/rules | 200 | 1.00 | 5.5 KiB | pass |
| Check a clean system draft: no error | POST /v1/prompts/check | 200 | 15.0 | 905 B | pass |
| Check a draft with an unknown template variable | POST /v1/prompts/check | 200 | 1.00 | 1.0 KiB | pass |
| Check an empty fragment: the empty rule is an error | POST /v1/prompts/check | 200 | 1.00 | 279 B | pass |
| Check an AGENTS.md draft | POST /v1/prompts/check | 200 | 2.00 | 1.3 KiB | pass |
| Refuse a check with a kind that does not exist | POST /v1/prompts/check | 422 | 1.00 | 60 B | pass |
| Refuse a check whose body is not JSON | POST /v1/prompts/check | 400 | 2.00 | 53 B | pass |
| Refuse a list filtered by a kind that does not exist | GET /v1/prompts?kind=bogus | 422 | 2.00 | 60 B | pass |
| List only the fragments | GET /v1/prompts?kind=fragment | 200 | 2.00 | 14 B | pass |
| Create a system prompt bound to a role | POST /v1/prompts | 201 | 40.0 | 322 B | pass |
| Refuse a second prompt with the same id | POST /v1/prompts | 409 | 9.00 | 55 B | pass |
| Refuse a second prompt on a role another prompt holds | POST /v1/prompts | 409 | 8.00 | 58 B | pass |
| Refuse a prompt of a kind that does not exist | POST /v1/prompts | 422 | 2.00 | 60 B | pass |
| Refuse a fragment bound to a role | POST /v1/prompts | 422 | 2.00 | 57 B | pass |
| Refuse a system prompt bound to plaza (plaza is for AGENTS.md) | POST /v1/prompts | 422 | 1.00 | 47 B | pass |
| Refuse a prompt id with a space | POST /v1/prompts | 422 | 5.00 | 98 B | pass |
| Refuse a title of 121 characters | POST /v1/prompts | 422 | 1.00 | 42 B | pass |
| Refuse a create without a body | POST /v1/prompts | 400 | 1.00 | 53 B | pass |
| Read the created prompt back: unpublished and unscored | GET /v1/prompts/t-prompt-life | 200 | 1.00 | 565 B | pass |
| Read version 1 with its findings and stats | GET /v1/prompts/t-prompt-life/versions/1 | 200 | 1.00 | 1.1 KiB | pass |
| Answer 404 for a version that does not exist | GET /v1/prompts/t-prompt-life/versions/2 | 404 | 2.00 | 37 B | pass |
| Answer 404 for a version that is not a number | GET /v1/prompts/t-prompt-life/versions/one | 404 | 1.00 | 37 B | pass |
| Download version 1 as Markdown | GET /v1/prompts/t-prompt-life/versions/1/raw | 200 | 1.00 | 70 B | pass |
| Answer 404 for the Markdown of an unknown prompt | GET /v1/prompts/no-such-prompt/versions/1/raw | 404 | 1.00 | 37 B | pass |
| Refuse a compare without both version numbers | GET /v1/prompts/t-prompt-life/diff | 422 | 1.00 | 47 B | pass |
| The history starts with the create | GET /v1/prompts/t-prompt-life/history | 200 | 1.00 | 228 B | pass |
| Refuse an edit to an empty title | PUT /v1/prompts/t-prompt-life | 422 | 1.00 | 42 B | pass |
| Refuse a system prompt rebound to plaza | PUT /v1/prompts/t-prompt-life | 422 | 1.00 | 47 B | pass |
| Answer 404 when editing an unknown prompt | PUT /v1/prompts/no-such-prompt | 404 | 1.00 | 37 B | pass |
| Edit the title, description, tags and keep the binding | PUT /v1/prompts/t-prompt-life | 200 | 16.0 | 317 B | pass |
| Add version 2 with a Rules section | POST /v1/prompts/t-prompt-life/versions | 201 | 10.0 | 243 B | pass |
| Send the same body again: the latest comes back, nothing new | POST /v1/prompts/t-prompt-life/versions | 200 | 2.00 | 244 B | pass |
| Refuse a version built on an old base (409) | POST /v1/prompts/t-prompt-life/versions | 409 | 33.0 | 48 B | pass |
| Add version 3 with an unknown variable: an error and a lower score | POST /v1/prompts/t-prompt-life/versions | 201 | 9.00 | 248 B | pass |
| Refuse a version whose body is not JSON | POST /v1/prompts/t-prompt-life/versions | 400 | 2.00 | 53 B | pass |
| Refuse a version without its base_version | POST /v1/prompts/t-prompt-life/versions | 400 | 2.00 | 53 B | pass |
| Answer 404 when adding a version to an unknown prompt | POST /v1/prompts/no-such-prompt/versions | 404 | 2.00 | 37 B | pass |
| The detail names the best version: never the broken one | GET /v1/prompts/t-prompt-life | 200 | 2.00 | 999 B | pass |
| Read the broken version: the finding names the rule | GET /v1/prompts/t-prompt-life/versions/3 | 200 | 1.00 | 1.2 KiB | pass |
| Download version 2 as Markdown | GET /v1/prompts/t-prompt-life/versions/2/raw | 200 | 1.00 | 131 B | pass |
| Compare version 1 with version 2 | GET /v1/prompts/t-prompt-life/diff?from=1&to=2 | 200 | 1.00 | 2.6 KiB | pass |
| Compare version 2 with the broken version 3 | GET /v1/prompts/t-prompt-life/diff?from=2&to=3 | 200 | 1.00 | 2.8 KiB | pass |
| Refuse a compare without the second version | GET /v1/prompts/t-prompt-life/diff?from=1 | 422 | 2.00 | 47 B | pass |
| Answer 404 when comparing with a version that does not exist | GET /v1/prompts/t-prompt-life/diff?from=1&to=99 | 404 | 2.00 | 37 B | pass |
| The history records each version written | GET /v1/prompts/t-prompt-life/history | 200 | 1.00 | 549 B | pass |
| Refuse to publish a version with an error | POST /v1/prompts/t-prompt-life/publish | 422 | 1.00 | 81 B | pass |
| Refuse to publish a version that does not exist | POST /v1/prompts/t-prompt-life/publish | 422 | 2.00 | 43 B | pass |
| Answer 404 when publishing an unknown prompt | POST /v1/prompts/no-such-prompt/publish | 404 | 2.00 | 37 B | pass |
| Refuse a publish whose body is not JSON | POST /v1/prompts/t-prompt-life/publish | 400 | 2.00 | 53 B | pass |
| Publish version 2 as the live one | POST /v1/prompts/t-prompt-life/publish | 200 | 19.0 | 312 B | pass |
| The live version shows what was published | GET /v1/prompts/t-prompt-life | 200 | 1.00 | 2.0 KiB | pass |
| The system list shows the live version and the best one | GET /v1/prompts?kind=system | 200 | 1.00 | 4.6 KiB | pass |
| Unpublish the prompt: no live version and no score | POST /v1/prompts/t-prompt-life/unpublish | 200 | 24.0 | 317 B | pass |
| Answer 404 when unpublishing an unknown prompt | POST /v1/prompts/no-such-prompt/unpublish | 404 | 11.0 | 37 B | pass |
| Publish the older version 1 again (a rollback) | POST /v1/prompts/t-prompt-life/publish | 200 | 15.0 | 312 B | pass |
| The history records publish and unpublish | GET /v1/prompts/t-prompt-life/history | 200 | 1.00 | 875 B | pass |
| Archive the prompt: its binding is released | POST /v1/prompts/t-prompt-life/archive | 200 | 8.00 | 321 B | pass |
| The default list leaves an archived prompt out | GET /v1/prompts | 200 | 2.00 | 4.3 KiB | pass |
| The archived list shows the archived prompt | GET /v1/prompts?archived=true | 200 | 1.00 | 4.6 KiB | pass |
| A second prompt takes the role the archive released | POST /v1/prompts | 201 | 25.0 | 261 B | pass |
| Refuse to restore while another prompt holds the role (409) | POST /v1/prompts/t-prompt-life/restore | 409 | 8.00 | 58 B | pass |
| Archive the second prompt: the role is free again | POST /v1/prompts/t-prompt-next/archive | 200 | 8.00 | 270 B | pass |
| Restore the first prompt once the role is free | POST /v1/prompts/t-prompt-life/restore | 200 | 12.0 | 312 B | pass |
| The restored prompt keeps its live version and binding | GET /v1/prompts/t-prompt-life | 200 | 1.00 | 2.1 KiB | pass |
| Answer 404 when restoring an unknown prompt | POST /v1/prompts/no-such-prompt/restore | 404 | 7.00 | 37 B | pass |
| Answer 404 when archiving an unknown prompt | POST /v1/prompts/no-such-prompt/archive | 404 | 7.00 | 37 B | pass |
| The history records the archive and the restore | GET /v1/prompts/t-prompt-life/history | 200 | 1.00 | 1.1 KiB | pass |
| Archive the test prompt | POST /v1/prompts/t-prompt-life/archive | 200 | 8.00 | 321 B | pass |
| Archive the second test prompt | POST /v1/prompts/t-prompt-next/archive | 200 | 10.0 | 270 B | pass |
| Neither test prompt is in the default list any more | GET /v1/prompts | 200 | 1.00 | 4.3 KiB | pass |
| Both test prompts are in the archived list | GET /v1/prompts?archived=true | 200 | 1.00 | 4.9 KiB | pass |
| List the built-in prompts | GET /v1/prompts | 200 | 2.00 | 4.3 KiB | pass |
| Create a system prompt | POST /v1/prompts | 201 | 16.0 | 282 B | pass |
| Read the created prompt back | GET /v1/prompts/planner | 200 | 1.00 | 525 B | pass |
| Edit the prompt's details | PUT /v1/prompts/planner | 200 | 19.0 | 291 B | pass |
| List the prompts after the edit | GET /v1/prompts | 200 | 1.00 | 4.6 KiB | pass |
| Archive the prompt | POST /v1/prompts/planner/archive | 200 | 19.0 | 300 B | pass |
| Answer 404 for an unknown prompt id | GET /v1/prompts/no-such-prompt | 404 | 1.00 | 37 B | pass |
| Reject a create body without a title | POST /v1/prompts | 400 | 1.00 | 53 B | pass |
rules
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Check a law source that compiles and allows | POST /v1/laws/check | 200 | 3.00 | 71 B | pass |
| Refuse a law source that does not compile, with its place | POST /v1/laws/check | 200 | 2.00 | 146 B | pass |
| Check a source that declares no law | POST /v1/laws/check | 200 | 3.00 | 141 B | pass |
| Refuse a check whose body is not a JSON object | POST /v1/laws/check | 400 | 6.00 | 42 B | pass |
| Refuse a rule that does not compile, with its place | POST /v1/laws/rules | 400 | 3.00 | 147 B | pass |
| Add an owner rule with a law | POST /v1/laws/rules | 201 | 33.0 | 329 B | pass |
| List the laws with the new owner law | GET /v1/laws | 200 | 2.00 | 106.9 KiB | pass |
| Disable the owner rule | POST /v1/laws/rules/R-001/disable | 200 | 52.0 | 330 B | pass |
| List the laws with the owner law switched off | GET /v1/laws | 200 | 2.00 | 106.9 KiB | pass |
| Enable the owner rule again | POST /v1/laws/rules/R-001/enable | 200 | 14.0 | 329 B | pass |
| Refuse an edit whose source does not compile, and keep the rule | PUT /v1/laws/rules/R-001 | 400 | 2.00 | 147 B | pass |
| Change an owner rule's title and its law | PUT /v1/laws/rules/R-001 | 200 | 10.0 | 332 B | pass |
| List the laws after the edit: the new law, not the old | GET /v1/laws | 200 | 2.00 | 106.8 KiB | pass |
| Answer 404 when enabling an unknown rule | POST /v1/laws/rules/R-bruno-none/enable | 404 | 1.00 | 40 B | pass |
| Answer 404 when editing an unknown rule | PUT /v1/laws/rules/R-bruno-none | 404 | 1.00 | 40 B | pass |
| Delete the owner rule and its law | DELETE /v1/laws/rules/R-001 | 200 | 26.0 | 32 B | pass |
| List the laws after the delete: the law is gone | GET /v1/laws | 200 | 2.00 | 106.2 KiB | pass |
| Refuse to switch a statute off without a reason | PUT /v1/laws/overrides/S-000a | 400 | 1.00 | 43 B | pass |
| Switch a statute off, with the reason on record | PUT /v1/laws/overrides/S-000a | 200 | 17.0 | 140 B | pass |
| List the laws with the statute switched off | GET /v1/laws | 200 | 2.00 | 106.3 KiB | pass |
| Switch the statute back on | PUT /v1/laws/overrides/S-000a | 200 | 9.00 | 45 B | pass |
| List the laws with the statute on again | GET /v1/laws | 200 | 3.00 | 106.2 KiB | pass |
| Answer 404 when switching an unknown law off | PUT /v1/laws/overrides/S-999z | 404 | 2.00 | 33 B | pass |
| Ask for the typed confirmation before a constitutional law goes off | PUT /v1/laws/overrides/C-001a | 409 | 1.00 | 86 B | pass |
| Refuse a confirmation that names another law | PUT /v1/laws/overrides/C-001a | 409 | 1.00 | 121 B | pass |
| Switch a constitutional law off with its typed id | PUT /v1/laws/overrides/C-001a | 200 | 42.0 | 124 B | pass |
| Read the rules: the law is off and the change is in the history | GET /v1/laws/rules | 200 | 1.00 | 368 B | pass |
| List the laws: the constitutional law is flagged off | GET /v1/laws | 200 | 2.00 | 106.3 KiB | pass |
| Switch the constitutional law back on, which needs no confirmation | PUT /v1/laws/overrides/C-001a | 200 | 27.0 | 45 B | pass |
| Read the rules: the law is on again and the change is in the history | GET /v1/laws/rules | 200 | 1.00 | 440 B | pass |
| List the owner's rules on an empty hub | GET /v1/laws/rules | 200 | 3.00 | 440 B | pass |
| Add a rule | POST /v1/laws/rules | 201 | 9.00 | 338 B | pass |
| List the rules after adding one | GET /v1/laws/rules | 200 | 1.00 | 765 B | pass |
| Rename a rule | PUT /v1/laws/rules/R-001 | 200 | 17.0 | 347 B | pass |
| List the rules after a rename | GET /v1/laws/rules | 200 | 0.00 | 774 B | pass |
| Delete a rule | DELETE /v1/laws/rules/R-001 | 200 | 9.00 | 32 B | pass |
| Answer 404 for an unknown rule | DELETE /v1/laws/rules/no-such-rule | 404 | 1.00 | 40 B | pass |
| Refuse a rule whose body is not a JSON object | POST /v1/laws/rules | 400 | 1.00 | 42 B | pass |
providers
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| read the settings and keep them | GET /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| switch the background health checks off | PUT /v1/settings | 200 | 2.00 | 1.2 KiB | pass |
| List the provider kinds you can add | GET /v1/provider-kinds | 200 | 2.00 | 8.7 KiB | pass |
| Show what the Add provider dialog offers | GET /v1/provider-catalog | 200 | 1.00 | 10.5 KiB | pass |
| List the models of every provider | GET /v1/models | 200 | 111 | 2.7 KiB | pass |
| Refuse a provider id with capitals and a space | POST /v1/providers | 400 | 1.00 | 42 B | pass |
| Refuse a provider of a kind that does not exist | POST /v1/providers | 400 | 1.00 | 288 B | pass |
| Refuse a provider accent that is not #rrggbb | POST /v1/providers | 400 | 2.00 | 43 B | pass |
| Add an OpenAI-compatible provider with a key slot and no key yet | POST /v1/providers | 201 | 2.00 | 384 B | pass |
| Refuse to add a provider whose id exists | POST /v1/providers | 409 | 1.00 | 43 B | pass |
| Check a provider with no key: critical, and nothing is called | POST /v1/providers/t-prov-compat/check | 200 | 1.00 | 119 B | pass |
| Set the provider's key: the view shows the slot, not the value | PUT /v1/providers/t-prov-compat/secret | 200 | 35.0 | 422 B | pass |
| Refuse an empty key | PUT /v1/providers/t-prov-compat/secret | 400 | 1.00 | 26 B | pass |
| Refuse a key body without a value field | PUT /v1/providers/t-prov-compat/secret | 400 | 1.00 | 45 B | pass |
| Answer 404 when setting the key of an unknown provider | PUT /v1/providers/no-such-provider/secret | 404 | 1.00 | 42 B | pass |
| Read the provider back: keyed, and no value in the view | GET /v1/providers/t-prov-compat | 200 | 2.00 | 422 B | pass |
| Check a keyed provider that is unreachable: critical, not a server error | POST /v1/providers/t-prov-compat/check | 200 | 2.00 | 169 B | pass |
| Test an unreachable provider's connection: the report says it failed, with the reason | POST /v1/providers/t-prov-compat/test | 200 | 3003 | 134 B | pass |
| List an unreachable provider's models: 502 with the reason | GET /v1/providers/t-prov-compat/models | 502 | 1.00 | 60 B | pass |
| Answer 404 when checking an unknown provider | POST /v1/providers/no-such-provider/check | 404 | 2.00 | 42 B | pass |
| Answer 404 when testing an unknown provider | POST /v1/providers/no-such-provider/test | 404 | 2.00 | 42 B | pass |
| The model listing reports the unreachable provider's error | GET /v1/models | 200 | 130 | 2.8 KiB | pass |
| Check every enabled provider: the new one is critical | POST /v1/providers/check | 200 | 3.00 | 1.8 KiB | pass |
| Replace a provider's label and accent: the key slot is kept | PUT /v1/providers/t-prov-compat | 200 | 2.00 | 462 B | pass |
| Refuse an accent with six characters after the # | PUT /v1/providers/t-prov-compat | 400 | 2.00 | 43 B | pass |
| Refuse a blank label | PUT /v1/providers/t-prov-compat | 400 | 1.00 | 72 B | pass |
| Refuse a label longer than 60 characters | PUT /v1/providers/t-prov-compat | 400 | 1.00 | 72 B | pass |
| Refuse a replace whose body id differs from the path | PUT /v1/providers/t-prov-compat | 400 | 2.00 | 50 B | pass |
| Answer 404 when replacing an unknown provider | PUT /v1/providers/no-such-provider | 404 | 1.00 | 42 B | pass |
| Refuse a replace with a kind that does not exist | PUT /v1/providers/t-prov-compat | 400 | 4.00 | 278 B | pass |
| Disable a provider: it keeps its settings and shows off | PUT /v1/providers/t-prov-compat | 200 | 1.00 | 363 B | pass |
| A disabled provider is not in the model listing | GET /v1/models | 200 | 86.0 | 2.7 KiB | pass |
| Enable the provider again | PUT /v1/providers/t-prov-compat | 200 | 2.00 | 462 B | pass |
| Delete the provider | DELETE /v1/providers/t-prov-compat | 200 | 1.00 | 27 B | pass |
| Answer 404 for a provider that was deleted | GET /v1/providers/t-prov-compat | 404 | 2.00 | 39 B | pass |
| Answer 404 when deleting a provider twice | DELETE /v1/providers/t-prov-compat | 404 | 0.00 | 39 B | pass |
| Answer 404 when setting the key of a deleted provider | PUT /v1/providers/t-prov-compat/secret | 404 | 1.00 | 39 B | pass |
| The provider list no longer has the deleted provider | GET /v1/providers | 200 | 1.00 | 7.0 KiB | pass |
| The model listing forgets the deleted provider | GET /v1/models | 200 | 79.0 | 2.7 KiB | pass |
| Check every enabled provider: the deleted one is not in it | POST /v1/providers/check | 200 | 1.00 | 1.6 KiB | pass |
| the settings go back to what they were before the folder | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| Find the scratch provider key in the vault, so this folder deletes it | GET /v1/vault/secrets | 200 | 2.00 | 1.8 KiB | pass |
| Delete the scratch provider key: the provider is gone, and its key is not kept | DELETE /v1/vault/secrets/sec_yf6zofcrephy | 204 | 25.0 | 0 B | pass |
| List the providers | GET /v1/providers | 200 | 2.00 | 7.0 KiB | pass |
| Add a provider | POST /v1/providers | 201 | 2.00 | 333 B | pass |
| Read the provider back | GET /v1/providers/scratch-provider | 200 | 1.00 | 333 B | pass |
| Replace the provider's settings | PUT /v1/providers/scratch-provider | 200 | 1.00 | 333 B | pass |
| List the providers again | GET /v1/providers | 200 | 4.00 | 7.4 KiB | pass |
| Delete the provider | DELETE /v1/providers/scratch-provider | 200 | 1.00 | 30 B | pass |
| Answer 404 for an unknown provider | GET /v1/providers/no-such-provider | 404 | 0.00 | 42 B | pass |
| Refuse a provider body that does not parse | POST /v1/providers | 400 | 0.00 | 85 B | pass |
| List the login bridge switches (off by default) | GET /v1/login-bridges | 200 | 1.00 | 665 B | pass |
| Switch a vendor's login bridge on and off again (owner only) | PUT /v1/login-bridges/openai | 200 | 8.00 | 320 B | pass |
| Refuse a login bridge for a vendor it is not offered for | PUT /v1/login-bridges/example | 404 | 2.00 | 41 B | pass |
vault
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the vault's entries before any secret of the owner's | GET /v1/vault/secrets | 200 | 2.00 | 1.4 KiB | pass |
| Store a secret with no binding | POST /v1/vault/secrets | 200 | 18.0 | 321 B | pass |
| List the stored entry without its value | GET /v1/vault/secrets | 200 | 39.0 | 1.7 KiB | pass |
| Refuse a test of a secret with no binding | POST /v1/vault/secrets/sec_qjhb37cobrrf/test | 400 | 1.00 | 46 B | pass |
| Bind a stored secret to a tool and an MCP server | PATCH /v1/vault/secrets/sec_qjhb37cobrrf | 200 | 15.0 | 364 B | pass |
| List the bound entry with its bindings | GET /v1/vault/secrets | 200 | 1.00 | 1.8 KiB | pass |
| Test a bound secret with its first binding | POST /v1/vault/secrets/sec_qjhb37cobrrf/test | 200 | 1.00 | 90 B | pass |
| Rotate the value of a bound secret | PUT /v1/vault/secrets/sec_qjhb37cobrrf/value | 200 | 14.0 | 441 B | pass |
| List the rotated entry at version 2 | GET /v1/vault/secrets | 200 | 1.00 | 1.8 KiB | pass |
| Roll the secret back to version 1 | POST /v1/vault/secrets/sec_qjhb37cobrrf/rollback | 200 | 41.0 | 441 B | pass |
| Refuse a rollback to a version that does not exist | POST /v1/vault/secrets/sec_qjhb37cobrrf/rollback | 404 | 0.00 | 26 B | pass |
| Refuse a rotation to an empty value | PUT /v1/vault/secrets/sec_qjhb37cobrrf/value | 400 | 1.00 | 30 B | pass |
| Unbind the secret from all its consumers | PATCH /v1/vault/secrets/sec_qjhb37cobrrf | 200 | 28.0 | 404 B | pass |
| Refuse a test once the secret has no binding | POST /v1/vault/secrets/sec_qjhb37cobrrf/test | 400 | 1.00 | 46 B | pass |
| Refuse a binding that is not one | PATCH /v1/vault/secrets/sec_qjhb37cobrrf | 400 | 1.00 | 91 B | pass |
| Delete the unbound secret | DELETE /v1/vault/secrets/sec_qjhb37cobrrf | 204 | 16.0 | 0 B | pass |
| The list no longer shows the deleted secret | GET /v1/vault/secrets | 200 | 2.00 | 1.4 KiB | pass |
| Refuse a second delete of the same secret | DELETE /v1/vault/secrets/sec_qjhb37cobrrf | 404 | 1.00 | 26 B | pass |
| Refuse a rotation of the deleted secret | PUT /v1/vault/secrets/sec_qjhb37cobrrf/value | 404 | 1.00 | 26 B | pass |
| Add a passphrase unlock slot | POST /v1/vault/slots | 200 | 761 | 340 B | pass |
| Refuse a passphrase slot with no passphrase | POST /v1/vault/slots | 400 | 1.00 | 48 B | pass |
| Refuse a rekey that leaves out the passphrase slot | POST /v1/vault/rekey | 400 | 1.00 | 88 B | pass |
| Rekey the vault with the passphrase of its slot | POST /v1/vault/rekey | 200 | 725 | 349 B | pass |
| Make a recovery code, shown once | POST /v1/vault/recovery | 200 | 28.0 | 84 B | pass |
| The status lists the recovery slot but not the code | GET /v1/vault | 200 | 2.00 | 442 B | pass |
| Seal the vault | POST /v1/vault/seal | 200 | 51.0 | 441 B | pass |
| Refuse a secret read while the vault is sealed | GET /v1/vault/secrets | 423 | 0.00 | 24 B | pass |
| Refuse an unlock with the wrong passphrase | POST /v1/vault/unseal | 401 | 583 | 45 B | pass |
| Unseal the vault with its passphrase | POST /v1/vault/unseal | 200 | 877 | 442 B | pass |
| Seal the vault again | POST /v1/vault/seal | 200 | 53.0 | 441 B | pass |
| Unseal the vault with the recovery code | POST /v1/vault/unseal | 200 | 23.0 | 442 B | pass |
| Seal the vault before the key file unseal | POST /v1/vault/seal | 200 | 143 | 441 B | pass |
| Unseal the vault from its key file | POST /v1/vault/unseal | 200 | 55.0 | 442 B | pass |
| Refuse to remove a slot that does not exist | DELETE /v1/vault/slots/slt_doesnotexist | 404 | 2.00 | 26 B | pass |
| Remove the recovery slot | DELETE /v1/vault/slots/slt_5wppilmuctkv | 200 | 48.0 | 349 B | pass |
| Remove the passphrase slot | DELETE /v1/vault/slots/slt_5jmsoqk2pzu3 | 200 | 34.0 | 254 B | pass |
| Refuse to remove the last key slot | DELETE /v1/vault/slots/slt_r2hc4yijcvxn | 409 | 1.00 | 46 B | pass |
| Import two secrets from a dotenv file | POST /v1/vault/import | 200 | 51.0 | 161 B | pass |
| List the imported entries with their bindings | GET /v1/vault/secrets | 200 | 2.00 | 2.1 KiB | pass |
| Read the consumers and the entry each one resolves | GET /v1/vault/consumers | 200 | 2.00 | 5.6 KiB | pass |
| Test an imported secret with its bound tool | POST /v1/vault/secrets/sec_jzchllqxm4b6/test | 200 | 2.00 | 90 B | pass |
| Delete the first imported secret | DELETE /v1/vault/secrets/sec_jzchllqxm4b6 | 204 | 50.0 | 0 B | pass |
| Delete the second imported secret | DELETE /v1/vault/secrets/sec_wlckupibm6n3 | 204 | 26.0 | 0 B | pass |
| Read the audit log and verify its chain | GET /v1/vault/audit?limit=500&verify=1 | 200 | 3.00 | 31.6 KiB | pass |
| The audit names each lifecycle event of the secret | GET /v1/vault/audit?limit=500 | 200 | 1.00 | 31.6 KiB | pass |
| Page the audit log from the start with a limit | GET /v1/vault/audit?after=0&limit=2 | 200 | 2.00 | 637 B | pass |
| Refuse an audit cursor that is not a number | GET /v1/vault/audit?after=abc | 400 | 0.00 | 34 B | pass |
| Refuse an audit page size that is not a number | GET /v1/vault/audit?limit=lots | 400 | 1.00 | 34 B | pass |
| Refuse a secret body that is not JSON | POST /v1/vault/secrets | 400 | 3.00 | 53 B | pass |
| Refuse a secret name in lower case | POST /v1/vault/secrets | 400 | 1.00 | 87 B | pass |
| Refuse a secret of an unknown kind | POST /v1/vault/secrets | 400 | 1.00 | 68 B | pass |
| Refuse an empty secret value | POST /v1/vault/secrets | 400 | 1.00 | 30 B | pass |
| Refuse a secret value over the size limit | POST /v1/vault/secrets | 400 | 1.00 | 53 B | pass |
| Refuse a binding that is not one | POST /v1/vault/secrets | 400 | 1.00 | 91 B | pass |
| Refuse a malformed node id in a scope | POST /v1/vault/secrets | 400 | 2.00 | 42 B | pass |
| Refuse a node scope that names a node not of this plaza | POST /v1/vault/secrets | 400 | 1.00 | 42 B | pass |
| Refuse a node scope with no node | POST /v1/vault/secrets | 400 | 1.00 | 49 B | pass |
| Refuse a label longer than 200 characters | POST /v1/vault/secrets | 400 | 1.00 | 51 B | pass |
| Create the first plaza entry of a name | POST /v1/vault/secrets | 200 | 31.0 | 307 B | pass |
| Refuse a second plaza entry for the same name | POST /v1/vault/secrets | 409 | 1.00 | 77 B | pass |
| Delete the first entry of the duplicate name | DELETE /v1/vault/secrets/sec_acbchin2zsw7 | 204 | 45.0 | 0 B | pass |
| Refuse a patch of an unknown secret | PATCH /v1/vault/secrets/sec_doesnotexist | 404 | 5.00 | 26 B | pass |
| Refuse a patch that is not JSON | PATCH /v1/vault/secrets/sec_doesnotexist | 400 | 1.00 | 53 B | pass |
| Refuse a rotation of an unknown secret | PUT /v1/vault/secrets/sec_doesnotexist/value | 404 | 1.00 | 26 B | pass |
| Refuse a test of an unknown secret | POST /v1/vault/secrets/sec_doesnotexist/test | 404 | 1.00 | 26 B | pass |
| Refuse a test on a node that is not of this plaza | POST /v1/vault/secrets/sec_doesnotexist/test | 400 | 1.00 | 42 B | pass |
| Refuse a rollback without a version number | POST /v1/vault/secrets/sec_doesnotexist/rollback | 400 | 1.00 | 53 B | pass |
| Refuse an unlock with both a passphrase and a recovery code | POST /v1/vault/unseal | 400 | 1.00 | 58 B | pass |
| Refuse a slot of an unknown kind | POST /v1/vault/slots | 400 | 5.00 | 48 B | pass |
| Refuse a second key file slot | POST /v1/vault/slots | 409 | 2.00 | 50 B | pass |
| Refuse an import in another format | POST /v1/vault/import | 400 | 1.00 | 39 B | pass |
| Refuse a vault read from a host that does not name loopback | GET /v1/vault/secrets | 403 | 6.00 | 46 B | pass |
| Refuse a duplicate name given twice in a dotenv file | POST /v1/vault/import | 200 | 1.00 | 139 B | pass |
| Show the vault's state | GET /v1/vault | 200 | 2.00 | 254 B | pass |
| Create a secret | POST /v1/vault/secrets | 200 | 39.0 | 333 B | pass |
| Change a secret's label | PATCH /v1/vault/secrets/sec_kmm43bvdkgqz | 200 | 15.0 | 342 B | pass |
| Read the secret's metadata back from the list | GET /v1/vault/secrets | 200 | 1.00 | 1.8 KiB | pass |
| Delete a secret | DELETE /v1/vault/secrets/sec_kmm43bvdkgqz | 204 | 63.0 | 0 B | pass |
| Answer 404 for an unknown secret id | DELETE /v1/vault/secrets/sec_doesnotexist | 404 | 2.00 | 26 B | pass |
| Refuse a secret body without a value | POST /v1/vault/secrets | 400 | 1.00 | 53 B | pass |
settings
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Read the hub's settings | GET /v1/settings | 200 | 2.00 | 1.2 KiB | pass |
| Save a valid change to the settings | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| Refuse a settings value out of range | PUT /v1/settings | 400 | 1.00 | 44 B | pass |
| Refuse a settings body that is not valid JSON for the type | PUT /v1/settings | 400 | 1.00 | 94 B | pass |
| Read the saved change back | GET /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| Check the session is signed in | GET /v1/auth/check | 200 | 1.00 | 11 B | pass |
| Refuse a sign-in with a wrong owner token | POST /v1/auth/session | 401 | 5.00 | 39 B | pass |
plaza
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Resume the plaza so it runs: the hold is off | POST /v1/plaza/resume | 200 | 2.00 | 80 B | pass |
| Read the plaza state: running | GET /v1/plaza/state | 200 | 1.00 | 80 B | pass |
| Stop all work and hold the plaza, with a reason | POST /v1/plaza/stop | 200 | 2.00 | 111 B | pass |
| Read the plaza state: held, with who held it and why | GET /v1/plaza/state | 200 | 1.00 | 111 B | pass |
| Refuse a decision call while the plaza is held | POST /v1/decisions | 409 | 3.00 | 73 B | pass |
| Create a project while the plaza is held: planning is not held | POST /v1/projects | 201 | 2.00 | 559 B | pass |
| Put the held project in maintenance | POST /v1/projects/pr1/mode | 200 | 1.00 | 655 B | pass |
| Refuse a maintenance sweep while the plaza is held | POST /v1/projects/pr1/maintenance/sweep | 409 | 1.00 | 43 B | pass |
| Stopping again keeps the hold | POST /v1/plaza/stop | 200 | 2.00 | 79 B | pass |
| Refuse a stop whose body is not JSON | POST /v1/plaza/stop | 400 | 1.00 | 93 B | pass |
| Resume the plaza: the hold ends | POST /v1/plaza/resume | 200 | 2.00 | 80 B | pass |
| Read the plaza state: running again | GET /v1/plaza/state | 200 | 1.00 | 80 B | pass |
| Decision calls are no longer refused for the hold: a missing key is the refusal | POST /v1/decisions | 502 | 15.0 | 101 B | pass |
| A sweep of the held project is refused for having no agents, not for the hold | POST /v1/projects/pr1/maintenance/sweep | 409 | 1.00 | 101 B | pass |
| Resuming a running plaza changes nothing | POST /v1/plaza/resume | 200 | 1.00 | 80 B | pass |
| Read the default decision model before any change | GET /v1/decisions/default | 200 | 0.00 | 30 B | pass |
| Refuse a default whose provider does not exist | PUT /v1/decisions/default | 400 | 1.00 | 30 B | pass |
| Refuse a default that is a chat provider, not a decision model | PUT /v1/decisions/default | 400 | 1.00 | 61 B | pass |
| Refuse a default model the decision provider does not list | PUT /v1/decisions/default | 400 | 4.00 | 61 B | pass |
| Refuse a default with no model | PUT /v1/decisions/default | 400 | 1.00 | 68 B | pass |
| Refuse a default body that is not JSON | PUT /v1/decisions/default | 400 | 1.00 | 96 B | pass |
| The refused defaults changed nothing | GET /v1/decisions/default | 200 | 0.00 | 30 B | pass |
| Refuse a decision call with no questions | POST /v1/decisions | 400 | 2.00 | 43 B | pass |
| Refuse a decision call with more than 128 questions | POST /v1/decisions | 400 | 1.00 | 45 B | pass |
| Refuse a decision call whose provider is a chat provider | POST /v1/decisions | 400 | 1.00 | 58 B | pass |
| Refuse a decision call for a model the provider does not list | POST /v1/decisions | 400 | 2.00 | 61 B | pass |
| Refuse a choice question with a single choice | POST /v1/decisions | 400 | 1.00 | 57 B | pass |
| Refuse a question with no instructions | POST /v1/decisions | 400 | 1.00 | 44 B | pass |
| Refuse two questions with the same name | POST /v1/decisions | 400 | 1.00 | 43 B | pass |
| Refuse a score question with one level | POST /v1/decisions | 400 | 1.00 | 55 B | pass |
| Refuse a body with no input | POST /v1/decisions | 400 | 1.00 | 64 B | pass |
| Refuse a decision body that is not JSON | POST /v1/decisions | 400 | 1.00 | 93 B | pass |
| A valid decision call goes to the gateway: without a key it is a 502 | POST /v1/decisions | 502 | 8.00 | 101 B | pass |
| List the tasks on an empty hub | GET /v1/tasks | 200 | 1.00 | 12 B | pass |
| Open a project for the tasks | POST /v1/projects | 201 | 10.0 | 690 B | pass |
| Create a task | POST /v1/tasks | 201 | 2.00 | 333 B | pass |
| Read the task back | GET /v1/tasks/t1 | 200 | 1.00 | 333 B | pass |
| List the tasks after a create | GET /v1/tasks | 200 | 0.00 | 345 B | pass |
| Withdraw an open task | DELETE /v1/tasks/t1 | 200 | 1.00 | 346 B | pass |
| Answer 404 for an unknown task | GET /v1/tasks/t999999 | 404 | 1.00 | 35 B | pass |
| Refuse a task with no goal | POST /v1/tasks | 400 | 0.00 | 75 B | pass |
| Read the board | GET /v1/board | 200 | 1.00 | 134 B | pass |
| Refuse a board post from an unknown agent | POST /v1/board | 404 | 1.00 | 37 B | pass |
| Refuse a board post with an empty subject | POST /v1/board | 400 | 1.00 | 37 B | pass |
| Create a task others will run after | POST /v1/tasks | 201 | 0.00 | 288 B | pass |
| Create a task that runs after another | POST /v1/tasks | 201 | 1.00 | 307 B | pass |
| List only the tasks that can be claimed now | GET /v1/tasks?ready=true | 200 | 1.00 | 300 B | pass |
| Refuse a ready filter that is not true or false | GET /v1/tasks?ready=maybe | 400 | 0.00 | 54 B | pass |
| Refuse links that would close a cycle | PATCH /v1/tasks/t2 | 400 | 1.00 | 80 B | pass |
| Relink an open task | PATCH /v1/tasks/t3 | 200 | 1.00 | 298 B | pass |
| Answer 404 for relinking an unknown task | PATCH /v1/tasks/t999999 | 404 | 1.00 | 35 B | pass |
| Refuse a task outside a project | POST /v1/tasks | 400 | 1.00 | 93 B | pass |
| Refuse a task for an unknown project | POST /v1/tasks | 404 | 1.00 | 38 B | pass |
library
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the library's tool versions | GET /v1/library | 200 | 2.00 | 2 B | pass |
| Answer 404 for files of an unknown tool version | GET /v1/library/no-such-tool/1/files | 404 | 0.00 | 27 B | pass |
| Answer 409 when reviewing an unknown tool version | POST /v1/library/no-such-tool/1/review | 409 | 1.00 | 27 B | pass |
| Answer 400 when a review has no approve field | POST /v1/library/no-such-tool/1/review | 400 | 1.00 | 54 B | pass |
| Answer 404 when deleting an unknown tool version | DELETE /v1/library/no-such-tool/1 | 404 | 1.00 | 27 B | pass |
| List the projects | GET /v1/projects | 200 | 1.00 | 1.6 KiB | pass |
| Answer 404 for an unknown project | GET /v1/projects/no-such-project | 404 | 1.00 | 27 B | pass |
| Answer 409 when approving an unknown project | POST /v1/projects/no-such-project/approve | 409 | 2.00 | 44 B | pass |
| Answer 404 for an unknown project's task graph | GET /v1/projects/no-such-project/graph | 404 | 1.00 | 27 B | pass |
| Pause a project | POST /v1/projects/pr2/pause | 200 | 1.00 | 1.0 KiB | pass |
| Resume a paused project | POST /v1/projects/pr2/resume | 200 | 1.00 | 1.1 KiB | pass |
| Refuse a project with no title | POST /v1/projects | 400 | 1.00 | 45 B | pass |
| Open a project to archive | POST /v1/projects | 201 | 23.0 | 650 B | pass |
| Archive a project | POST /v1/projects/pr3/archive | 200 | 1.00 | 725 B | pass |
| Refuse a task for an archived project | POST /v1/tasks | 409 | 1.00 | 58 B | pass |
| Refuse to resume an archived project | POST /v1/projects/pr3/resume | 409 | 0.00 | 35 B | pass |
| Answer 404 when pausing an unknown project | POST /v1/projects/no-such-project/pause | 404 | 1.00 | 27 B | pass |
| Answer 404 when moving an unknown task into a project | POST /v1/tasks/t999/move | 404 | 1.00 | 32 B | pass |
| Refuse to move a task out of its project | POST /v1/tasks/t1/move | 409 | 1.00 | 70 B | pass |
| Refuse a move that names no project | POST /v1/tasks/t1/move | 400 | 1.00 | 46 B | pass |
| Read a project's settings, none set yet | GET /v1/projects/pr2/settings | 200 | 0.00 | 2 B | pass |
| Override a project's effort and task budget | PATCH /v1/projects/pr2/settings | 200 | 1.00 | 36 B | pass |
| Refuse an effort the plaza does not know | PATCH /v1/projects/pr2/settings | 400 | 1.00 | 101 B | pass |
| Refuse a setting projects do not have | PATCH /v1/projects/pr2/settings | 400 | 0.00 | 510 B | pass |
| Show where each of a project's settings comes from | GET /v1/projects/pr2/settings/effective | 200 | 1.00 | 750 B | pass |
| Set a project's effort back to the plaza's | PATCH /v1/projects/pr2/settings | 200 | 1.00 | 20 B | pass |
| Answer 404 for an unknown project's settings | GET /v1/projects/no-such-project/settings | 404 | 1.00 | 27 B | pass |
| Answer 404 when recording tests of an unknown tool version | POST /v1/library/no-such-tool/1/tests | 409 | 1.00 | 27 B | pass |
| Refuse to promote an unknown tool version | POST /v1/library/no-such-tool/1/promote | 409 | 1.00 | 27 B | pass |
| Refuse to stage an unknown replacement | POST /v1/library/no-such-tool/1/stage | 409 | 2.00 | 27 B | pass |
| Propose removal of harmful and unused tools | POST /v1/library/sweep | 200 | 1.00 | 11 B | pass |
mcp
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the MCP servers on an empty hub | GET /v1/mcp/servers | 200 | 1.00 | 2 B | pass |
| Add an MCP server at a fake address | POST /v1/mcp/servers | 200 | 1.00 | 482 B | pass |
| Refuse a server with an id that is already taken | POST /v1/mcp/servers | 409 | 1.00 | 68 B | pass |
| Refuse a plain-http address while private addresses are off | POST /v1/mcp/servers | 400 | 1.00 | 105 B | pass |
| List the catalog of MCP server presets | GET /v1/mcp/catalog | 200 | 1.00 | 4.8 KiB | pass |
| List the servers and find the added one | GET /v1/mcp/servers | 200 | 1.00 | 484 B | pass |
| Read the added server back | GET /v1/mcp/servers/bru-fake-mcp | 200 | 1.00 | 482 B | pass |
| Test the server and refuse its tools while it is unreachable | POST /v1/mcp/servers/bru-fake-mcp/test | 200 | 1.00 | 107 B | pass |
| List the tools of an unreachable server: none | GET /v1/mcp/servers/bru-fake-mcp/tools | 200 | 3.00 | 2 B | pass |
| Start the unreachable server and report its error | POST /v1/mcp/servers/bru-fake-mcp/start | 200 | 1.00 | 515 B | pass |
| Read the logs of the unreachable server | GET /v1/mcp/servers/bru-fake-mcp/logs?limit=5 | 200 | 1.00 | 2 B | pass |
| Stop the unreachable server | POST /v1/mcp/servers/bru-fake-mcp/stop | 200 | 0.00 | 515 B | pass |
| Refuse the logs of an unknown server | GET /v1/mcp/servers/no-such-mcp/logs | 404 | 0.00 | 52 B | pass |
| Rename the server and change its limits | PATCH /v1/mcp/servers/bru-fake-mcp | 200 | 2.00 | 514 B | pass |
| Refuse a change of the server's id | PATCH /v1/mcp/servers/bru-fake-mcp | 400 | 1.00 | 48 B | pass |
| Refuse a change to a plain-http address while private addresses are off | PATCH /v1/mcp/servers/bru-fake-mcp | 400 | 1.00 | 105 B | pass |
| Refuse a change to an unknown server | PATCH /v1/mcp/servers/no-such-mcp | 404 | 1.00 | 52 B | pass |
| Refuse a change that is not a JSON object | PATCH /v1/mcp/servers/bru-fake-mcp | 400 | 1.00 | 79 B | pass |
| Add a server whose header reads a vault secret | POST /v1/mcp/servers | 200 | 1.00 | 547 B | pass |
| The vault lists the server as a consumer of that secret | GET /v1/vault/consumers | 200 | 1.00 | 5.8 KiB | pass |
| Refuse a header whose format has no placeholder | POST /v1/mcp/servers | 400 | 1.00 | 111 B | pass |
| Refuse a stdio server given a container image | POST /v1/mcp/servers | 400 | 0.00 | 140 B | pass |
| Delete the fake server | DELETE /v1/mcp/servers/bru-fake-mcp | 204 | 1.00 | 0 B | pass |
| Refuse to read the deleted server | GET /v1/mcp/servers/bru-fake-mcp | 404 | 1.00 | 53 B | pass |
| Refuse a second delete of the same server | DELETE /v1/mcp/servers/bru-fake-mcp | 404 | 0.00 | 53 B | pass |
| Delete the server with the vault header | DELETE /v1/mcp/servers/bru-vault-mcp | 204 | 1.00 | 0 B | pass |
| The deleted servers are gone from the list | GET /v1/mcp/servers | 200 | 1.00 | 2 B | pass |
| Show the hub's MCP server as switched off | GET /v1/hub-mcp | 200 | 3.00 | 263 B | pass |
| Refuse an MCP message while the server is off | POST /mcp | 404 | 1.00 | 28 B | pass |
| Refuse a GET on the MCP endpoint while the server is off | GET /mcp | 404 | 1.00 | 28 B | pass |
| Refuse a settings change with an unknown bind | PUT /v1/hub-mcp | 400 | 1.00 | 35 B | pass |
| Refuse settings that are not JSON | PUT /v1/hub-mcp | 400 | 1.00 | 101 B | pass |
| Refuse an unknown auth mode | PUT /v1/hub-mcp | 400 | 1.00 | 39 B | pass |
| Refuse an unknown open scope | PUT /v1/hub-mcp | 400 | 1.00 | 52 B | pass |
| Refuse an origin that carries a path | PUT /v1/hub-mcp | 400 | 1.00 | 86 B | pass |
| Refuse remote binding while the hub is not on TLS | PUT /v1/hub-mcp | 400 | 1.00 | 59 B | pass |
| Turn the hub's MCP server on, locally, in secure mode | PUT /v1/hub-mcp | 200 | 39.0 | 262 B | pass |
| Refuse an MCP message with no token in secure mode | POST /mcp | 401 | 1.00 | 38 B | pass |
| Refuse an MCP message from a foreign origin | POST /mcp | 403 | 2.00 | 57 B | pass |
| Refuse a bad token even while the server is on | POST /mcp | 401 | 2.00 | 25 B | pass |
| Answer 405 to a GET on the MCP endpoint while it is on | GET /mcp | 405 | 1.00 | 38 B | pass |
| Refuse remote binding over TLS-less listener even while on | PUT /v1/hub-mcp | 400 | 2.00 | 59 B | pass |
| Refuse an open-mode anonymous scope that is not a scope | PUT /v1/hub-mcp | 400 | 1.00 | 52 B | pass |
| Create an observe token | POST /v1/hub-mcp/tokens | 201 | 43.0 | 253 B | pass |
| Create an operate token | POST /v1/hub-mcp/tokens | 201 | 58.0 | 253 B | pass |
| Create a govern token | POST /v1/hub-mcp/tokens | 201 | 53.0 | 251 B | pass |
| List the tokens without their values | GET /v1/hub-mcp/tokens | 200 | 1.00 | 526 B | pass |
| Refuse a token of an unknown scope | POST /v1/hub-mcp/tokens | 400 | 1.00 | 47 B | pass |
| Refuse an observe token that names an operate tool | POST /v1/hub-mcp/tokens | 400 | 1.00 | 54 B | pass |
| Refuse a token with an empty name | POST /v1/hub-mcp/tokens | 400 | 1.00 | 38 B | pass |
| Refuse a token that expires in zero days | POST /v1/hub-mcp/tokens | 400 | 1.00 | 37 B | pass |
| Initialize an MCP session with the observe token | POST /mcp | 200 | 2.00 | 267 B | pass |
| List the tools an observe token may see | POST /mcp | 200 | 2.00 | 10.8 KiB | pass |
| Call an observe tool with the observe token | POST /mcp | 200 | 54.0 | 396 B | pass |
| Refuse an operate tool called with the observe token | POST /mcp | 200 | 39.0 | 86 B | pass |
| Refuse a govern tool called with the observe token | POST /mcp | 200 | 28.0 | 88 B | pass |
| Initialize an MCP session with the operate token | POST /mcp | 200 | 1.00 | 267 B | pass |
| List the tools an operate token may see | POST /mcp | 200 | 4.00 | 28.2 KiB | pass |
| Refuse a govern tool called with the operate token | POST /mcp | 200 | 72.0 | 88 B | pass |
| Refuse a call with no session | POST /mcp | 400 | 2.00 | 71 B | pass |
| Refuse a call with an unknown session | POST /mcp | 404 | 1.00 | 56 B | pass |
| Answer a parse error for a message that is not JSON | POST /mcp | 200 | 2.00 | 75 B | pass |
| Read the call log of the observe token | GET /v1/hub-mcp/calls?token=mcp_605e21c29c41&limit=50 | 200 | 2.00 | 673 B | pass |
| Refuse a call-log limit that is not a number | GET /v1/hub-mcp/calls?limit=lots | 400 | 1.00 | 29 B | pass |
| Filter the call log by tool and limit | GET /v1/hub-mcp/calls?tool=plaza_status&limit=5 | 200 | 2.00 | 225 B | pass |
| Revoke the operate token | DELETE /v1/hub-mcp/tokens/mcp_cf553e7a2115 | 204 | 47.0 | 0 B | pass |
| Refuse a second revoke of the same token | DELETE /v1/hub-mcp/tokens/mcp_cf553e7a2115 | 404 | 12.0 | 37 B | pass |
| Refuse a call with the revoked token | POST /mcp | 401 | 2.00 | 25 B | pass |
| List the revoked token with its revocation time | GET /v1/hub-mcp/tokens | 200 | 1.00 | 544 B | pass |
| Keep the observe token working after the operate token is revoked | POST /mcp | 200 | 2.00 | 10.8 KiB | pass |
| Refuse to revoke a token that does not exist | DELETE /v1/hub-mcp/tokens/tok_doesnotexist | 404 | 14.0 | 37 B | pass |
| Switch the hub's MCP server off again | PUT /v1/hub-mcp | 200 | 8.00 | 263 B | pass |
| Refuse an observe token's call while the server is off | POST /mcp | 404 | 1.00 | 28 B | pass |
| Show the server as off with its live tokens | GET /v1/hub-mcp | 200 | 1.00 | 263 B | pass |
| Revoke the observe token | DELETE /v1/hub-mcp/tokens/mcp_605e21c29c41 | 204 | 14.0 | 0 B | pass |
| Revoke the govern token | DELETE /v1/hub-mcp/tokens/mcp_efe5a1e88d48 | 204 | 8.00 | 0 B | pass |
| Every token of this area is revoked at the end | GET /v1/hub-mcp/tokens | 200 | 1.00 | 562 B | pass |
| Add an MCP server | POST /v1/mcp/servers | 200 | 3.00 | 553 B | pass |
| Read the MCP server back | GET /v1/mcp/servers/test-server | 200 | 1.00 | 553 B | pass |
| Change an MCP server's name | PATCH /v1/mcp/servers/test-server | 200 | 1.00 | 556 B | pass |
| Answer 404 for an unknown MCP server | GET /v1/mcp/servers/no-such-server | 404 | 1.00 | 55 B | pass |
| Refuse an MCP server with an invalid id | POST /v1/mcp/servers | 400 | 1.00 | 110 B | pass |
| Delete an MCP server | DELETE /v1/mcp/servers/test-server | 204 | 0.00 | 0 B | pass |
| Create an MCP token | POST /v1/hub-mcp/tokens | 201 | 112 | 252 B | pass |
| List the MCP tokens | GET /v1/hub-mcp/tokens | 200 | 1.00 | 733 B | pass |
| Revoke an MCP token | DELETE /v1/hub-mcp/tokens/mcp_6f32b2750496 | 204 | 42.0 | 0 B | pass |
metrics
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the metric scopes | GET /v1/metrics/scopes | 200 | 2.00 | 6.3 KiB | pass |
| Compose the alerts tab over a week | GET /v1/metrics/view?tab=alerts&range=7d&scope=global | 200 | 4.00 | 3.6 KiB | pass |
| Compose the capacity tab zoomed to a part of the month | GET /v1/metrics/view?tab=capacity&range=30d&zoom=0.25,0.75&width=400&tz=60&compare=prev | 200 | 2.00 | 3.2 KiB | pass |
| Compose the tokens tab from the built-in fixture | GET /v1/metrics/view?tab=tokens&range=24h&fixture=1 | 200 | 100 | 57.3 KiB | pass |
| Refuse a view with an unknown tab | GET /v1/metrics/view?tab=nowhere | 400 | 3.00 | 63 B | pass |
| Refuse a view with a zero width | GET /v1/metrics/view?tab=overview&width=0 | 400 | 0.00 | 41 B | pass |
| Refuse a view wider than 4000 pixels | GET /v1/metrics/view?tab=overview&width=4001 | 400 | 1.00 | 44 B | pass |
| Refuse a zoom whose ends are out of order | GET /v1/metrics/view?tab=overview&zoom=0.8,0.2 | 400 | 0.00 | 80 B | pass |
| Refuse a view whose shift is not a number | GET /v1/metrics/view?tab=overview&shift=soon | 400 | 0.00 | 48 B | pass |
| Show what changed around a time | GET /v1/metrics/around?tab=overview&range=1h&t=1000 | 200 | 2.00 | 187 B | pass |
| Refuse an around-time that is not a number | GET /v1/metrics/around?tab=overview&range=1h&t=now | 400 | 1.00 | 43 B | pass |
| Export a fixture view as CSV | GET /v1/metrics/export?tab=overview&range=1h&fixture=1 | 200 | 13.0 | 81.0 KiB | pass |
| Refuse a CSV export with an unknown range | GET /v1/metrics/export?tab=overview&range=2w | 400 | 1.00 | 75 B | pass |
| Read a series grouped by entity | POST /v1/metrics/query | 200 | 1.00 | 13 B | pass |
| Read a series grouped by the model label | POST /v1/metrics/query | 200 | 0.00 | 13 B | pass |
| Read a series grouped by the role property | POST /v1/metrics/query | 200 | 1.00 | 13 B | pass |
| Refuse a grouping with an empty label | POST /v1/metrics/query | 400 | 1.00 | 87 B | pass |
| Refuse a query with a zero step | POST /v1/metrics/query | 400 | 1.00 | 76 B | pass |
| Refuse a query that ends before it starts | POST /v1/metrics/query | 400 | 1.00 | 76 B | pass |
| Read a series with a step as large as a u64 | POST /v1/metrics/query | 200 | 1.00 | 13 B | pass |
| Answer no groups for an unknown metric | POST /v1/metrics/query | 200 | 1.00 | 13 B | pass |
| Read a series for a scope that does not exist | POST /v1/metrics/query | 200 | 1.00 | 13 B | pass |
| Keep only the top group of a query | POST /v1/metrics/query | 200 | 5.00 | 13 B | pass |
| Refuse a query with an unknown aggregate | POST /v1/metrics/query | 400 | 2.00 | 183 B | pass |
| Refuse a query without an aggregate | POST /v1/metrics/query | 400 | 2.00 | 73 B | pass |
| Refuse a query body that is not JSON | POST /v1/metrics/query | 400 | 1.00 | 138 B | pass |
| List the events of the global scope | GET /v1/metrics/events?scope=global&limit=5 | 200 | 1.00 | 2 B | pass |
| List events with a limit above the cap and kind filters | GET /v1/metrics/events?scope=global&from=0&to=3600000&limit=99999&kinds=lifecycle,alert | 200 | 1.00 | 2 B | pass |
| Refuse an events window whose start is not a number | GET /v1/metrics/events?scope=global&from=yesterday | 400 | 1.00 | 52 B | pass |
| List the agents' calls with a limit | GET /v1/metrics/calls?scope=global&limit=10 | 200 | 2.00 | 2 B | pass |
| Refuse a calls page with a before that is not a number | GET /v1/metrics/calls?scope=global&before=x | 400 | 1.00 | 46 B | pass |
| Compose the next page of calls from the fixture | GET /v1/metrics/calls/page?tab=agents&range=24h&fixture=1 | 200 | 1.00 | 18.8 KiB | pass |
| Refuse a call page whose before is not a number | GET /v1/metrics/calls/page?before=x | 400 | 1.00 | 46 B | pass |
| List the kept traces | GET /v1/metrics/traces?scope=global&limit=5 | 200 | 2.00 | 2 B | pass |
| Refuse a trace window whose start is not a number | GET /v1/metrics/traces?scope=global&from=x | 400 | 1.00 | 44 B | pass |
| Answer 404 for a trace that was not kept | GET /v1/metrics/traces/no-such-trace | 404 | 2.00 | 43 B | pass |
| List the service-level objectives | GET /v1/metrics/slos | 200 | 2.00 | 785 B | pass |
| Read the capacity forecast panel over 30 days | GET /v1/metrics/forecast?range=30d | 200 | 2.00 | 187 B | pass |
| Refuse a forecast with an unknown range | GET /v1/metrics/forecast?range=2w | 400 | 1.00 | 75 B | pass |
| Scrape the hub's Prometheus metrics | GET /v1/metrics | 200 | 1.00 | 5.1 KiB | pass |
| Renew the live lease for a console view | POST /v1/metrics/live | 200 | 0.00 | 11 B | pass |
| Refuse a live lease without its tab and range | POST /v1/metrics/live | 400 | 1.00 | 73 B | pass |
| Mark agent 1 as open in the traces | POST /v1/metrics/open/1 | 200 | 1.00 | 11 B | pass |
| Refuse to open an agent whose id is not a number | POST /v1/metrics/open/first | 400 | 1.00 | 41 B | pass |
| Refuse a query whose tiny step over a long window would make too many points | POST /v1/metrics/query | 400 | 1.00 | 122 B | pass |
| Read the default metrics settings | GET /v1/metrics/settings | 200 | 1.00 | 292 B | pass |
| Save changed settings and see them repaired | PUT /v1/metrics/settings | 200 | 17.0 | 321 B | pass |
| Read back the saved settings | GET /v1/metrics/settings | 200 | 0.00 | 321 B | pass |
| Refuse settings with an unknown level | PUT /v1/metrics/settings | 400 | 1.00 | 129 B | pass |
| Refuse settings that are not JSON | PUT /v1/metrics/settings | 400 | 1.00 | 130 B | pass |
| Switch OpenMetrics on and receive its token once | PUT /v1/metrics/settings | 200 | 1.00 | 362 B | pass |
| Refuse the OpenMetrics scrape without a token | GET /metrics | 401 | 1.00 | 53 B | pass |
| Refuse the OpenMetrics scrape with a wrong token | GET /metrics | 401 | 1.00 | 53 B | pass |
| Scrape OpenMetrics with its token | GET /metrics | 200 | 2.00 | 84.0 KiB | pass |
| Save OpenMetrics on again without a new token | PUT /v1/metrics/settings | 200 | 10.0 | 291 B | pass |
| Scrape again with the same token | GET /metrics | 200 | 2.00 | 84.0 KiB | pass |
| Switch OpenMetrics off | PUT /v1/metrics/settings | 200 | 2.00 | 292 B | pass |
| Answer 404 for the scrape once it is off again | GET /metrics | 404 | 2.00 | 21 B | pass |
| Restore the default settings | PUT /v1/metrics/settings | 200 | 2.00 | 292 B | pass |
| Read the restored settings | GET /v1/metrics/settings | 200 | 1.00 | 292 B | pass |
| List the alerts with only the starter rules | GET /v1/metrics/alerts | 200 | 2.00 | 2.3 KiB | pass |
| Refuse an alert rule without its metric | PUT /v1/metrics/alerts/rules | 400 | 1.00 | 77 B | pass |
| Refuse an alert rule with an unknown severity | PUT /v1/metrics/alerts/rules | 400 | 1.00 | 113 B | pass |
| Answer 404 when switching a rule that does not exist | POST /v1/metrics/alerts/no-such-rule/on | 404 | 2.00 | 24 B | pass |
| Refuse to delete a rule that does not exist | DELETE /v1/metrics/alerts/rules/no-such-rule | 400 | 1.00 | 24 B | pass |
| Refuse to delete a starter rule | DELETE /v1/metrics/alerts/rules/starter-ttft | 400 | 1.00 | 44 B | pass |
| Switch a starter rule off and keep its threshold | PUT /v1/metrics/alerts/rules | 200 | 11.0 | 275 B | pass |
| Switch the starter rule back on | PUT /v1/metrics/alerts/rules | 200 | 2.00 | 274 B | pass |
| Add an owner's alert rule | PUT /v1/metrics/alerts/rules | 200 | 2.00 | 247 B | pass |
| List the alerts and find the added rule | GET /v1/metrics/alerts | 200 | 1.00 | 2.6 KiB | pass |
| Switch the owner's rule off | POST /v1/metrics/alerts/rule-1/on | 200 | 27.0 | 248 B | pass |
| Switch the owner's rule back on | POST /v1/metrics/alerts/rule-1/on | 200 | 1.00 | 247 B | pass |
| Save the owner's rule again with a new threshold | PUT /v1/metrics/alerts/rules | 200 | 1.00 | 255 B | pass |
| Mute the owner's rule until 2100 | POST /v1/metrics/alerts/rule-1/mute | 200 | 1.00 | 11 B | pass |
| Acknowledge the owner's rule for the global scope | POST /v1/metrics/alerts/rule-1/ack | 200 | 2.00 | 11 B | pass |
| List the alerts and see the mute, the ack and their history | GET /v1/metrics/alerts | 200 | 1.00 | 2.7 KiB | pass |
| Refuse an acknowledgement without a subject | POST /v1/metrics/alerts/rule-1/ack | 400 | 1.00 | 77 B | pass |
| Refuse a mute without an end time | POST /v1/metrics/alerts/rule-1/mute | 400 | 1.00 | 78 B | pass |
| Delete the owner's rule | DELETE /v1/metrics/alerts/rules/rule-1 | 200 | 2.00 | 11 B | pass |
| Refuse to delete the same rule twice | DELETE /v1/metrics/alerts/rules/rule-1 | 400 | 1.00 | 24 B | pass |
| List the alerts without the deleted rule | GET /v1/metrics/alerts | 200 | 2.00 | 2.4 KiB | pass |
| Answer 404 when switching a deleted rule | POST /v1/metrics/alerts/rule-1/on | 404 | 1.00 | 24 B | pass |
| Read the saved dashboards before any save | GET /v1/metrics/dashboards | 200 | 1.00 | 50 B | pass |
| Save the dashboards with a pinned scope | PUT /v1/metrics/dashboards | 200 | 9.00 | 67 B | pass |
| Read back the saved dashboards | GET /v1/metrics/dashboards | 200 | 1.00 | 67 B | pass |
| Refuse dashboards whose scopes are not a list | PUT /v1/metrics/dashboards | 400 | 3.00 | 107 B | pass |
| Clear the saved dashboard pins again | PUT /v1/metrics/dashboards | 200 | 2.00 | 51 B | pass |
| Show the price of a model a vendor publishes | GET /v1/prices/meta/muse-wonder-1.0 | 200 | 1.00 | 122 B | pass |
| Show a vendor price with the model id in capitals | GET /v1/prices/META/muse-wonder-1.0 | 200 | 2.00 | 122 B | pass |
| Answer 404 for an encoded model with no price | GET /v1/prices/no/such-model-xyz | 404 | 2.00 | 42 B | pass |
| Refresh the model prices and list both sources | POST /v1/prices/refresh | 200 | 120 | 520.2 KiB | pass |
| Show the vendor price after a refresh | GET /v1/prices/meta/muse-wonder-1.0 | 200 | 1.00 | 122 B | pass |
| Read the metric catalog | GET /v1/metrics/catalog | 200 | 1.00 | 27.3 KiB | pass |
| Compose the overview view on an empty hub | GET /v1/metrics/view?tab=overview&range=24h | 200 | 6.00 | 3.6 KiB | pass |
| Refuse a view with an unknown range | GET /v1/metrics/view?tab=overview&range=2w | 400 | 2.00 | 75 B | pass |
| Read a metric series grouped by all | POST /v1/metrics/query | 200 | 1.00 | 13 B | pass |
| Refuse a query with an unknown group | POST /v1/metrics/query | 400 | 1.00 | 88 B | pass |
| Read the metrics settings | GET /v1/metrics/settings | 200 | 4.00 | 292 B | pass |
| Answer 404 for the scrape endpoint while OpenMetrics is off | GET /metrics | 404 | 2.00 | 21 B | pass |
| Summarize usage over a valid window | GET /v1/usage/summary?from=0&to=1000 | 200 | 2.00 | 231 B | pass |
| Refuse a usage window that ends before it starts | GET /v1/usage/summary?from=2000&to=1000 | 400 | 1.00 | 42 B | pass |
| List the model prices | GET /v1/prices | 200 | 7.00 | 520.2 KiB | pass |
| Answer 404 for a model with no price | GET /v1/prices/no-such-model-xyz | 404 | 1.00 | 42 B | pass |
joining
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the node join codes on an empty hub | GET /v1/nodes/codes | 200 | 1.00 | 2 B | pass |
| Issue a join code for a new node | POST /v1/nodes/codes | 201 | 1.00 | 221 B | pass |
| List the join codes shows the new code as active | GET /v1/nodes/codes | 200 | 1.00 | 120 B | pass |
| Revoke an unused node join code | DELETE /v1/nodes/codes/c1 | 200 | 1.00 | 26 B | pass |
| List the join codes after revoking shows the code as revoked | GET /v1/nodes/codes | 200 | 0.00 | 121 B | pass |
| Answer 404 when revoking an unknown node join code | DELETE /v1/nodes/codes/c999999 | 404 | 0.00 | 24 B | pass |
| Answer 400 when the node join code body is not JSON | POST /v1/nodes/codes | 400 | 1.00 | 111 B | pass |
| Refuse a node join with a bad join code | POST /v1/nodes/join | 403 | 1.00 | 39 B | pass |
| List the workstation join codes on an empty hub | GET /v1/stations/codes | 200 | 0.00 | 2 B | pass |
| Answer 400 when the workstation join code body is not JSON | POST /v1/stations/codes | 400 | 1.00 | 111 B | pass |
| Answer 404 when revoking an unknown workstation join code | DELETE /v1/stations/codes/c999999 | 404 | 1.00 | 24 B | pass |
| Refuse a workstation join with a bad join code | POST /v1/stations/join | 403 | 1.00 | 95 B | pass |
| Answer 404 when stopping an unknown node | POST /v1/nodes/n999/stop | 404 | 1.00 | 24 B | pass |
| Answer 404 when resuming an unknown node | POST /v1/nodes/n999/resume | 404 | 2.00 | 24 B | pass |
| Answer 400 when a transfer names no files | POST /v1/transfers | 400 | 1.00 | 46 B | pass |
| Stopping the hub's own node n0 takes it out of service | POST /v1/nodes/n0/stop | 200 | 15.0 | 679 B | pass |
| Answer 502 when a transfer names an unknown node | POST /v1/transfers | 502 | 1.00 | 24 B | pass |
| Resuming the hub's own node n0 puts it back in service | POST /v1/nodes/n0/resume | 200 | 21.0 | 679 B | pass |
| A dry-run rebalance lists its moves and starts none | POST /v1/nodes/rebalance | 200 | 4.00 | 28 B | pass |
| List the test lab on a fresh hub | GET /v1/test-lab | 200 | 1.00 | 250 B | pass |
| Answer 404 for an unknown test-lab run | GET /v1/test-lab/runs/q999 | 404 | 1.00 | 23 B | pass |
| Answer 404 when setting the roles of an unknown node | POST /v1/nodes/n999/roles | 404 | 2.00 | 24 B | pass |
| Refuse a collector's logs for an unknown test-lab run | POST /v1/test-lab/runs/q999/otlp/logs | 404 | 1.00 | 23 B | pass |
| Answer 404 for the observations of an unknown test-lab run | GET /v1/test-lab/runs/q999/observations | 404 | 1.00 | 23 B | pass |
| Answer 404 for assessing an unknown test-lab run | POST /v1/test-lab/runs/q999/assess | 404 | 2.00 | 23 B | pass |
| Answer 404 for the performance of an unknown test-lab run | GET /v1/test-lab/runs/q999/performance | 404 | 2.00 | 23 B | pass |
| Answer 404 for the UI checks of an unknown test-lab run | POST /v1/test-lab/runs/q999/ux | 404 | 1.00 | 23 B | pass |
| Refuse a scrubbed copy for an unknown test-lab run | POST /v1/test-lab/runs/q999/scrub | 404 | 2.00 | 23 B | pass |
| Answer 404 for the scrubbed copies of an unknown test-lab run | GET /v1/test-lab/runs/q999/scrub | 404 | 1.00 | 23 B | pass |
agents
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the agents on a hub with no node | GET /v1/agents | 200 | 0.00 | 2 B | pass |
| Answer 404 for an agent that does not exist | GET /v1/agents/999999 | 404 | 1.00 | 25 B | pass |
| Answer 400 for an agent id that is not a number | GET /v1/agents/not-a-number | 400 | 1.00 | 32 B | pass |
| Answer 404 when deleting an agent that does not exist | DELETE /v1/agents/999999 | 404 | 2.00 | 25 B | pass |
| List the roles before any custom role exists | GET /v1/roles | 200 | 1.00 | 44.0 KiB | pass |
| Refuse a custom role with missing fields | POST /v1/roles | 400 | 1.00 | 62 B | pass |
| Create a custom role | POST /v1/roles | 200 | 1.00 | 267 B | pass |
| Read the custom role back from the list | GET /v1/roles | 200 | 3.00 | 44.4 KiB | pass |
| Change a custom role by saving it again | POST /v1/roles | 200 | 1.00 | 311 B | pass |
| Delete a custom role | DELETE /v1/roles/scout-test | 200 | 1.00 | 34 B | pass |
| Answer 404 when deleting a role that is already gone | DELETE /v1/roles/scout-test | 404 | 1.00 | 30 B | pass |
| Answer 404 for an unknown project's roster | GET /v1/agents?project=no-such-project | 404 | 1.00 | 38 B | pass |
| Answer 404 when deleting a custom role that does not exist | DELETE /v1/roles/t-agents-lead | 404 | 1.00 | 33 B | pass |
| Refuse a custom role with no responsibilities | POST /v1/roles | 400 | 1.00 | 52 B | pass |
| Refuse a custom role with no deliverables | POST /v1/roles | 400 | 1.00 | 49 B | pass |
| Refuse a custom role with an empty title | POST /v1/roles | 400 | 1.00 | 26 B | pass |
| Refuse a custom role whose title is over 60 characters | POST /v1/roles | 400 | 1.00 | 46 B | pass |
| Refuse a custom role whose purpose is over 600 characters | POST /v1/roles | 400 | 1.00 | 49 B | pass |
| Refuse a custom role whose purpose is only spaces | POST /v1/roles | 400 | 1.00 | 28 B | pass |
| Refuse a custom role whose id is one character | POST /v1/roles | 400 | 1.00 | 82 B | pass |
| Refuse a custom role whose id is over 32 characters | POST /v1/roles | 400 | 0.00 | 82 B | pass |
| Refuse a custom role whose id has capital letters | POST /v1/roles | 400 | 1.00 | 82 B | pass |
| Refuse a custom role that takes a built-in role's id | POST /v1/roles | 409 | 1.00 | 38 B | pass |
| Refuse a custom role body that is not JSON | POST /v1/roles | 400 | 1.00 | 55 B | pass |
| Answer 404 when deleting a built-in role | DELETE /v1/roles/architect | 404 | 1.00 | 29 B | pass |
| The refused custom roles were not saved | GET /v1/roles | 200 | 2.00 | 44.0 KiB | pass |
| Create a custom role with unicode text | POST /v1/roles | 200 | 2.00 | 324 B | pass |
| Read the new custom role back in the list | GET /v1/roles | 200 | 1.00 | 44.4 KiB | pass |
| A spawn of the custom role reaches noded and is refused | POST /v1/agents | 502 | 1.00 | 108 B | pass |
| The custom role still has no agents after the refused spawn | GET /v1/roles | 200 | 1.00 | 44.4 KiB | pass |
| Make sure the custom role exists before it changes | POST /v1/roles | 200 | 1.00 | 324 B | pass |
| Change a custom role by saving it again with a new title | POST /v1/roles | 200 | 1.00 | 281 B | pass |
| Read the changed role back | GET /v1/roles | 200 | 0.00 | 44.4 KiB | pass |
| Refuse a change that empties the purpose | POST /v1/roles | 400 | 4.00 | 28 B | pass |
| The refused change leaves the saved title | GET /v1/roles | 200 | 1.00 | 44.4 KiB | pass |
| Make sure the custom role exists before it is deleted | POST /v1/roles | 200 | 1.00 | 324 B | pass |
| Delete a custom role | DELETE /v1/roles/t-agents-lead | 200 | 1.00 | 37 B | pass |
| The deleted role is no longer listed | GET /v1/roles | 200 | 1.00 | 44.0 KiB | pass |
| Answer 404 when deleting the role again | DELETE /v1/roles/t-agents-lead | 404 | 0.00 | 33 B | pass |
| Refuse a spawn of the deleted role | POST /v1/agents | 400 | 1.00 | 38 B | pass |
| Answer 404 for an agent that does not exist when read | GET /v1/agents/999999 | 404 | 0.00 | 25 B | pass |
| Answer 404 when renaming an agent that does not exist | PATCH /v1/agents/999999 | 404 | 1.00 | 25 B | pass |
| Answer 404 for the context of an agent that does not exist | GET /v1/agents/999999/context | 404 | 1.00 | 25 B | pass |
| Answer 404 for the transcript of an agent that does not exist | GET /v1/agents/999999/transcript | 404 | 1.00 | 25 B | pass |
| Answer 404 for the VM checkpoints of an agent that does not exist | GET /v1/agents/999999/vm-checkpoints | 404 | 1.00 | 27 B | pass |
| Answer 404 when checkpointing an agent that does not exist | POST /v1/agents/999999/vm-checkpoints | 404 | 1.00 | 27 B | pass |
| Answer 404 when deleting a checkpoint of an agent that does not exist | DELETE /v1/agents/999999/vm-checkpoints/c1 | 404 | 5.00 | 27 B | pass |
| Answer 404 when restoring a checkpoint of an agent that does not exist | POST /v1/agents/999999/vm-checkpoints/c1/restore | 404 | 1.00 | 27 B | pass |
| Answer 404 when forking an agent that does not exist | POST /v1/agents/999999/fork | 404 | 1.00 | 27 B | pass |
| Answer 404 when moving an agent that does not exist | POST /v1/agents/999999/move | 404 | 1.00 | 25 B | pass |
| Answer 409 when steering an agent that does not exist | POST /v1/agents/999999/control | 409 | 1.00 | 27 B | pass |
| Answer 404 when restarting an agent that does not exist | POST /v1/agents/999999/restart | 404 | 2.00 | 27 B | pass |
| Answer 404 when wiping an agent that does not exist | POST /v1/agents/999999/wipe | 404 | 0.00 | 27 B | pass |
| Answer 404 when retiring an agent that does not exist | POST /v1/agents/999999/retire | 404 | 1.00 | 27 B | pass |
| Answer 404 when putting an agent that does not exist to sleep | POST /v1/agents/999999/sleep | 404 | 1.00 | 25 B | pass |
| Answer 404 when waking an agent that does not exist | POST /v1/agents/999999/wake | 404 | 1.00 | 25 B | pass |
| Answer 404 when stopping an agent that does not exist | POST /v1/agents/999999/stop | 404 | 1.00 | 25 B | pass |
| Answer 404 when messaging an agent that does not exist | POST /v1/agents/999999/tell | 404 | 1.00 | 25 B | pass |
| Answer 404 when chatting with an agent that does not exist | POST /v1/agents/999999/chat | 404 | 0.00 | 25 B | pass |
| Answer 404 when assigning a task to an agent that does not exist | POST /v1/agents/999999/assign | 404 | 1.00 | 25 B | pass |
| Answer 404 when cancelling the work of an agent that does not exist | POST /v1/agents/999999/cancel | 404 | 1.00 | 25 B | pass |
| An agent that does not exist has the default limits | GET /v1/agents/999999/limits | 200 | 3.00 | 56 B | pass |
| Show the default permission mode for an agent that is not on the hub | GET /v1/agents/999999/permissions | 200 | 1.00 | 40 B | pass |
| Refuse a permission mode that is not one of the three | PUT /v1/agents/999999/permissions | 400 | 1.00 | 77 B | pass |
| Refuse a spawn of a role that does not exist | POST /v1/agents | 400 | 2.00 | 37 B | pass |
| Refuse a spawn of a role whose id has capitals | POST /v1/agents | 400 | 1.00 | 34 B | pass |
| Refuse a spawn of zero agents | POST /v1/agents | 400 | 1.00 | 32 B | pass |
| Refuse a spawn of 17 agents | POST /v1/agents | 400 | 1.00 | 32 B | pass |
| Refuse a spawn whose count is text | POST /v1/agents | 400 | 0.00 | 82 B | pass |
| Refuse a spawn with no role | POST /v1/agents | 400 | 1.00 | 61 B | pass |
| Refuse a spawn with a model that has no provider | POST /v1/agents | 400 | 1.00 | 74 B | pass |
| Refuse a spawn on a node that does not exist | POST /v1/agents | 400 | 1.00 | 27 B | pass |
| Refuse a spawn whose body is not JSON | POST /v1/agents | 400 | 1.00 | 61 B | pass |
| A spawn of an architect reaches noded and is refused | POST /v1/agents | 502 | 2.00 | 108 B | pass |
| A spawn of the orchestrator reaches noded and is refused | POST /v1/agents | 502 | 2.00 | 108 B | pass |
| Refuse an agent's hello without the internal token | POST /internal/v1/agents/999999/hello | 403 | 1.00 | 43 B | pass |
| Refuse an agent's hello with a wrong internal token | POST /internal/v1/agents/999999/hello | 403 | 1.00 | 43 B | pass |
| Refuse an agent's hello whose CID is not a number | POST /internal/v1/agents/abc/hello | 400 | 1.00 | 36 B | pass |
| Refuse an agent's hello whose VM is not a number | POST /internal/v1/agents/999999/hello | 400 | 1.00 | 72 B | pass |
| Refuse a GET of an agent's hello | GET /internal/v1/agents/999999/hello | 405 | 1.00 | 38 B | pass |
| Refuse the limits of an agent whose id is not a number | GET /v1/agents/abc/limits | 400 | 1.00 | 24 B | pass |
| Refuse setting the permission mode of an agent that is not on the hub | PUT /v1/agents/999999/permissions | 404 | 1.00 | 25 B | pass |
| The agent's VM says hello with the internal token | POST /internal/v1/agents/41001/hello | 200 | 10.0 | 55.0 KiB | pass |
| A registered agent is listed | GET /v1/agents | 200 | 1.00 | 397 B | pass |
| Read the registered agent | GET /v1/agents/41001 | 200 | 1.00 | 395 B | pass |
| A registered agent's context is empty | GET /v1/agents/41001/context | 200 | 2.00 | 351 B | pass |
| A registered agent's transcript is empty | GET /v1/agents/41001/transcript | 200 | 1.00 | 2 B | pass |
| A registered agent has no limits set | GET /v1/agents/41001/limits | 200 | 1.00 | 56 B | pass |
| A registered agent's permission mode is the default | GET /v1/agents/41001/permissions | 200 | 2.00 | 40 B | pass |
| Make sure the agent is registered before it is configured | POST /internal/v1/agents/41001/hello | 200 | 7.00 | 55.0 KiB | pass |
| Give the agent its own model | PATCH /v1/agents/41001 | 200 | 3.00 | 414 B | pass |
| Refuse a model name with no provider | PATCH /v1/agents/41001 | 400 | 1.00 | 65 B | pass |
| Refuse a model that is not text | PATCH /v1/agents/41001 | 400 | 1.00 | 42 B | pass |
| Clear the agent's model | PATCH /v1/agents/41001 | 200 | 6.00 | 395 B | pass |
| Refuse a rename whose body is not JSON | PATCH /v1/agents/41001 | 400 | 2.00 | 55 B | pass |
| Set the agent's permission mode to ask | PUT /v1/agents/41001/permissions | 200 | 21.0 | 27 B | pass |
| Read the permission mode back, now set for this agent | GET /v1/agents/41001/permissions | 200 | 1.00 | 31 B | pass |
| Refuse a permission mode that is not one of the three | PUT /v1/agents/41001/permissions | 400 | 1.00 | 77 B | pass |
| Restore the permission mode to autonomous | PUT /v1/agents/41001/permissions | 200 | 11.0 | 34 B | pass |
| Set the agent's turn limit | POST /v1/agents/41001/control | 200 | 5.00 | 82 B | pass |
| Read the turn limit back | GET /v1/agents/41001/limits | 200 | 1.00 | 53 B | pass |
| Refuse a compress threshold that is out of range | POST /v1/agents/41001/control | 409 | 2.00 | 67 B | pass |
| Show the agent's control state | POST /v1/agents/41001/control | 200 | 3.00 | 130 B | pass |
| Refuse an unknown control action | POST /v1/agents/41001/control | 409 | 1.00 | 91 B | pass |
| Refuse a control request with no action | POST /v1/agents/41001/control | 409 | 0.00 | 86 B | pass |
| Refuse a steer when the agent has no running work | POST /v1/agents/41001/control | 409 | 0.00 | 43 B | pass |
| Refuse a steer with no text | POST /v1/agents/41001/control | 409 | 1.00 | 28 B | pass |
| Refuse a compress when the agent has no running work | POST /v1/agents/41001/control | 409 | 1.00 | 43 B | pass |
| Refuse extending turns when the agent has no running work | POST /v1/agents/41001/control | 409 | 1.00 | 43 B | pass |
| Refuse extending turns by zero | POST /v1/agents/41001/control | 409 | 1.00 | 50 B | pass |
| Cancel an agent with no work in progress | POST /v1/agents/41001/cancel | 200 | 1.00 | 19 B | pass |
| Make sure the first agent is registered | POST /internal/v1/agents/41001/hello | 200 | 6.00 | 55.0 KiB | pass |
| Register the second agent from its VM | POST /internal/v1/agents/41002/hello | 200 | 6.00 | 55.0 KiB | pass |
| Make sure the agent is registered before its work is refused | POST /internal/v1/agents/41001/hello | 200 | 4.00 | 55.0 KiB | pass |
| Refuse an assignment of a task that does not exist | POST /v1/agents/41001/assign | 404 | 1.00 | 24 B | pass |
| Refuse an assignment that names no task | POST /v1/agents/41001/assign | 400 | 1.00 | 64 B | pass |
| Refuse to stop an agent whose node cannot be reached | POST /v1/agents/41001/stop | 502 | 1.00 | 101 B | pass |
| Refuse a VM checkpoint of an agent with no VM | POST /v1/agents/41001/vm-checkpoints | 409 | 1.00 | 46 B | pass |
| List the VM checkpoints of an agent with no VM | GET /v1/agents/41001/vm-checkpoints | 409 | 1.00 | 46 B | pass |
| Refuse restoring a checkpoint of an agent with no VM | POST /v1/agents/41001/vm-checkpoints/c1/restore | 409 | 1.00 | 46 B | pass |
| Refuse deleting a checkpoint of an agent with no VM | DELETE /v1/agents/41001/vm-checkpoints/c1 | 409 | 1.00 | 46 B | pass |
| Refuse forking an agent with no VM | POST /v1/agents/41001/fork | 409 | 1.00 | 46 B | pass |
| Refuse a fork of zero agents | POST /v1/agents/41001/fork | 400 | 1.00 | 28 B | pass |
| Refuse a move to a node that does not exist | POST /v1/agents/41001/move | 404 | 1.00 | 32 B | pass |
| Refuse a move that names no node | POST /v1/agents/41001/move | 400 | 1.00 | 61 B | pass |
| Refuse putting an agent with no VM to sleep | POST /v1/agents/41001/sleep | 409 | 1.00 | 46 B | pass |
| Refuse waking an agent that is not asleep | POST /v1/agents/41001/wake | 409 | 1.00 | 47 B | pass |
| Refuse restarting an agent with no VM | POST /v1/agents/41001/restart | 409 | 3.00 | 46 B | pass |
| Refuse retiring an agent with no VM | POST /v1/agents/41001/retire | 409 | 1.00 | 46 B | pass |
| Refuse wiping an agent with no VM | POST /v1/agents/41001/wipe | 409 | 1.00 | 46 B | pass |
| Refuse a wipe whose body is not JSON | POST /v1/agents/41001/wipe | 400 | 1.00 | 55 B | pass |
| Make sure the agent is registered before its session ends | POST /internal/v1/agents/41001/hello | 200 | 4.00 | 55.0 KiB | pass |
| Refuse an agent's session-ended report without the internal token | POST /internal/v1/agents/41001/gone | 403 | 1.00 | 43 B | pass |
| Record that the agent's session has ended | POST /internal/v1/agents/41001/gone | 200 | 1.00 | 2 B | pass |
| Read the agent offline | GET /v1/agents/41001 | 200 | 1.00 | 398 B | pass |
| Refuse a chat message to an offline agent | POST /v1/agents/41001/chat | 409 | 1.00 | 28 B | pass |
| The agent's VM says hello again | POST /internal/v1/agents/41001/hello | 200 | 5.00 | 55.0 KiB | pass |
| The agent is idle after its hello again | GET /v1/agents/41001 | 200 | 1.00 | 395 B | pass |
| Make sure the first agent is registered before it is deleted | POST /internal/v1/agents/41001/hello | 200 | 5.00 | 55.0 KiB | pass |
| Make sure the peer is registered before it is deleted | POST /internal/v1/agents/41002/hello | 200 | 5.00 | 55.0 KiB | pass |
| Delete an agent and its run | DELETE /v1/agents/41001 | 200 | 1.00 | 34 B | pass |
| Delete the peer agent | DELETE /v1/agents/41002 | 200 | 1.00 | 34 B | pass |
| Refuse deleting an agent whose id is not a number | DELETE /v1/agents/abc | 400 | 1.00 | 32 B | pass |
| The deleted agent is off the roster | GET /v1/agents/41001 | 404 | 1.00 | 25 B | pass |
| The deleted peer is off the roster | GET /v1/agents/41002 | 404 | 1.00 | 25 B | pass |
| List the agent proposals | GET /v1/agent-proposals | 200 | 1.00 | 2 B | pass |
| Refuse to reject a proposal that does not exist | POST /v1/agent-proposals/ap-none/reject | 409 | 2.00 | 31 B | pass |
| Refuse to reject a proposal that does not exist when no reason is sent | POST /v1/agent-proposals/ap-none/reject | 409 | 0.00 | 31 B | pass |
| Refuse a reject whose body is not JSON for a proposal that does not exist | POST /v1/agent-proposals/ap-none/reject | 409 | 2.00 | 31 B | pass |
| Refuse to approve a proposal that does not exist | POST /v1/agent-proposals/ap-none/approve | 409 | 1.00 | 31 B | pass |
| Refuse an approval whose role is text | POST /v1/agent-proposals/ap-none/approve | 400 | 1.00 | 73 B | pass |
| Refuse an approval whose role has no title | POST /v1/agent-proposals/ap-none/approve | 400 | 1.00 | 43 B | pass |
| Refuse an approval whose body is not JSON for a proposal that does not exist | POST /v1/agent-proposals/ap-none/approve | 409 | 1.00 | 31 B | pass |
governance
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Refuse an agent's hello without the internal token | POST /internal/v1/agents/8601/hello | 403 | 1.00 | 43 B | pass |
| Register a test agent through the internal API | POST /internal/v1/agents/8601/hello | 200 | 5.00 | 55.0 KiB | pass |
| Register a second test agent, which stays off any project | POST /internal/v1/agents/8602/hello | 200 | 4.00 | 55.0 KiB | pass |
| Open a project for the board tests | POST /v1/projects | 201 | 10.0 | 663 B | pass |
| Post to the board as the owner, for the project | POST /v1/board/posts | 201 | 1.00 | 245 B | pass |
| Post to the board as an agent on a project | POST /v1/board | 201 | 1.00 | 244 B | pass |
| Answer 404 for a post by an unknown agent | POST /v1/board | 404 | 1.00 | 40 B | pass |
| Refuse an agent's post whose body is not an object | POST /v1/board | 400 | 1.00 | 95 B | pass |
| Refuse an owner post with an empty body | POST /v1/board/posts | 400 | 1.00 | 34 B | pass |
| Refuse an owner post that mentions no agent | POST /v1/board/posts | 400 | 1.00 | 81 B | pass |
| Refuse an owner post for an unknown project | POST /v1/board/posts | 400 | 3.00 | 30 B | pass |
| Read the board for the project: both posts are there | GET /v1/board?project=pr4 | 200 | 1.00 | 437 B | pass |
| Comment as the owner on the agent's post | POST /v1/board/p2/comments | 201 | 1.00 | 135 B | pass |
| Refuse an empty comment | POST /v1/board/p2/comments | 400 | 1.00 | 39 B | pass |
| Answer 404 for a comment on an unknown post | POST /v1/board/p-none/comments | 404 | 1.00 | 26 B | pass |
| Read the board: the comment is on the agent's post | GET /v1/board | 200 | 2.00 | 816 B | pass |
| Mark the board read by an agent up to its own post | POST /v1/board/read | 200 | 1.00 | 42 B | pass |
| Refuse a read up to a post that is past the newest | POST /v1/board/read | 409 | 1.00 | 52 B | pass |
| Answer 404 for a read by an unknown agent | POST /v1/board/read | 404 | 1.00 | 40 B | pass |
| Pause the board's deliveries for the project | POST /v1/board/pause | 200 | 1.00 | 101 B | pass |
| Post as an agent while the project's deliveries are paused: stored | POST /v1/board | 201 | 1.00 | 229 B | pass |
| Post as the owner while the project's deliveries are paused: stored | POST /v1/board/posts | 201 | 1.00 | 240 B | pass |
| Refuse a pause for an unknown project | POST /v1/board/pause | 400 | 1.00 | 30 B | pass |
| Refuse a pause that does not say on or off | POST /v1/board/pause | 400 | 1.00 | 72 B | pass |
| Resume the project's deliveries | POST /v1/board/pause | 200 | 1.00 | 98 B | pass |
| Pause the whole board's deliveries | POST /v1/board/pause | 200 | 3.00 | 94 B | pass |
| Resume the whole board's deliveries | POST /v1/board/pause | 200 | 1.00 | 97 B | pass |
| Export the project's board thread as Markdown | GET /v1/board/export | 200 | 1.00 | 531 B | pass |
| Export the project's board thread as JSON | GET /v1/board/export?format=json | 200 | 0.00 | 1.1 KiB | pass |
| Refuse an export in a format other than md or json | GET /v1/board/export?project=pr4&format=xml | 400 | 1.00 | 46 B | pass |
| Refuse an export of an unknown project | GET /v1/board/export?project=pr-none | 400 | 1.00 | 30 B | pass |
| Mark the first test agent gone | POST /internal/v1/agents/8601/gone | 200 | 2.00 | 2 B | pass |
| Mark the second test agent gone | POST /internal/v1/agents/8602/gone | 200 | 1.00 | 2 B | pass |
| Register a test agent that holds no council seat | POST /internal/v1/agents/8603/hello | 200 | 10.0 | 55.0 KiB | pass |
| Open a project for the motion tests | POST /v1/projects | 201 | 26.0 | 668 B | pass |
| Mark the test agent gone | POST /internal/v1/agents/8603/gone | 200 | 1.00 | 2 B | pass |
| List the approvals on an empty hub | GET /v1/approvals | 200 | 1.00 | 2 B | pass |
| Register agent 7 through its VM's hello, for the permission mode checks | POST /internal/v1/agents/7/hello | 200 | 4.00 | 55.0 KiB | pass |
| Read an agent's permission mode before any change | GET /v1/agents/7/permissions | 200 | 2.00 | 40 B | pass |
| Set an agent's permission mode to read_only | PUT /v1/agents/7/permissions | 200 | 23.0 | 33 B | pass |
| Read back the permission mode after the change | GET /v1/agents/7/permissions | 200 | 1.00 | 37 B | pass |
| Refuse a permission mode that is not one of the three | PUT /v1/agents/7/permissions | 400 | 2.00 | 77 B | pass |
| Decide an approval that does not exist | POST /v1/approvals/no-such-approval | 404 | 1.00 | 28 B | pass |
| Delete agent 7 again, so later folders don't see it | DELETE /v1/agents/7 | 200 | 1.00 | 34 B | pass |
| Refuse a default decision model without a provider and model | PUT /v1/decisions/default | 400 | 1.00 | 68 B | pass |
| List the agent proposals on an empty hub | GET /v1/agent-proposals | 200 | 1.00 | 2 B | pass |
| Approve an agent proposal that does not exist | POST /v1/agent-proposals/no-such-proposal/approve | 409 | 1.00 | 40 B | pass |
foundry
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the recipes and read the seeded rung recipe's pin | GET /v1/foundry/recipes | 200 | 1.00 | 541 B | pass |
| Refuse a vet whose commit is not hex | POST /v1/foundry/recipes/rung-predictor@1/vet | 400 | 1.00 | 44 B | pass |
| Refuse a vet with no commit | POST /v1/foundry/recipes/rung-predictor@1/vet | 400 | 1.00 | 44 B | pass |
| Refuse a vet of a recipe that does not exist | POST /v1/foundry/recipes/no-such-recipe@1/vet | 404 | 1.00 | 38 B | pass |
| Refuse to vet the seeded dev recipe at another commit | POST /v1/foundry/recipes/rung-predictor@1/vet | 403 | 1.00 | 60 B | pass |
| Vet the seeded dev recipe at its own pin | POST /v1/foundry/recipes/rung-predictor@1/vet | 200 | 1.00 | 535 B | pass |
| The vet is kept in the recipe list | GET /v1/foundry/recipes | 200 | 1.00 | 537 B | pass |
| Upload 20 rows of two classes as a dataset | POST /v1/foundry/datasets/upload?name=t-ds-clean | 201 | 19.0 | 483 B | pass |
| List the datasets and find the clean upload | GET /v1/foundry/datasets | 200 | 5.00 | 485 B | pass |
| Upload rows with a column the format does not know | POST /v1/foundry/datasets/upload?name=t-ds-unknown | 201 | 157 | 504 B | pass |
| Upload an empty body as a dataset | POST /v1/foundry/datasets/upload?name=t-ds-empty | 201 | 14.0 | 430 B | pass |
| Upload rows that all carry one label | POST /v1/foundry/datasets/upload?name=t-ds-oneclass | 201 | 27.0 | 507 B | pass |
| Refuse an upload with a line that is not JSON | POST /v1/foundry/datasets/upload?name=t-ds-bad | 400 | 1.00 | 63 B | pass |
| Refuse an upload row with no timestamp | POST /v1/foundry/datasets/upload?name=t-ds-bad | 400 | 2.00 | 56 B | pass |
| Refuse an upload row with an unknown label | POST /v1/foundry/datasets/upload?name=t-ds-bad | 400 | 2.00 | 66 B | pass |
| Refuse an upload row whose number is a string | POST /v1/foundry/datasets/upload?name=t-ds-bad | 400 | 2.00 | 55 B | pass |
| Refuse an upload with no name and a body that is not JSON | POST /v1/foundry/datasets/upload | 400 | 1.00 | 51 B | pass |
| Refuse a named upload from a body with no name or path | POST /v1/foundry/datasets/upload | 400 | 2.00 | 41 B | pass |
| Refuse an upload from a relative path | POST /v1/foundry/datasets/upload | 400 | 2.00 | 28 B | pass |
| Refuse an upload from an absolute path that is not there | POST /v1/foundry/datasets/upload | 400 | 1.00 | 85 B | pass |
| An upload with an empty name takes a dated name | POST /v1/foundry/datasets/upload?name= | 201 | 17.0 | 510 B | pass |
| Export a cascade dataset from the plaza's records | POST /v1/foundry/datasets | 201 | 15.0 | 2.4 KiB | pass |
| Refuse an export of an unknown kind | POST /v1/foundry/datasets | 400 | 5.00 | 76 B | pass |
| Refuse an export whose body is not JSON | POST /v1/foundry/datasets | 400 | 1.00 | 58 B | pass |
| An export with no name is kept (the name is optional) | POST /v1/foundry/datasets | 201 | 28.0 | 2.4 KiB | pass |
| The export appears in the dataset list | GET /v1/foundry/datasets | 200 | 1.00 | 3.9 KiB | pass |
| Upload a clean dataset for the job | POST /v1/foundry/datasets/upload?name=t-job-clean | 201 | 21.0 | 484 B | pass |
| Plan a job with no GPU machines and see it refused | POST /v1/foundry/plan | 422 | 1.00 | 131 B | pass |
| Submit the job and see it refused without GPU machines | POST /v1/foundry/jobs | 422 | 2.00 | 131 B | pass |
| Read the refused job: there is no such job | GET /v1/foundry/jobs/t-job-nogpu | 404 | 1.00 | 23 B | pass |
| The job list does not hold the refused job | GET /v1/foundry/jobs | 200 | 1.00 | 2 B | pass |
| Cancel the refused job: there is no such job | POST /v1/foundry/jobs/t-job-nogpu/cancel | 404 | 1.00 | 23 B | pass |
| Resubmit the refused id: it is still free, so the refusal repeats | POST /v1/foundry/jobs | 422 | 2.00 | 131 B | pass |
| Upload a dataset with a column the format does not know | POST /v1/foundry/datasets/upload?name=t-job-unknown | 201 | 21.0 | 505 B | pass |
| Plan a job on the flagged dataset and see it refused | POST /v1/foundry/plan | 409 | 1.00 | 93 B | pass |
| Submit a job on the flagged dataset and see it refused | POST /v1/foundry/jobs | 409 | 1.00 | 93 B | pass |
| The flagged job is not recorded | GET /v1/foundry/jobs/t-job-taint | 404 | 3.00 | 23 B | pass |
| Upload an empty dataset | POST /v1/foundry/datasets/upload?name=t-job-empty | 201 | 16.0 | 431 B | pass |
| Submit a job on the empty dataset and see it refused | POST /v1/foundry/jobs | 409 | 2.00 | 81 B | pass |
| The empty job is not recorded | GET /v1/foundry/jobs/t-job-empty | 404 | 1.00 | 23 B | pass |
| Upload a dataset whose rows all carry one label | POST /v1/foundry/datasets/upload?name=t-job-oneclass | 201 | 27.0 | 508 B | pass |
| Submit a job on the one-label dataset and see it refused | POST /v1/foundry/jobs | 409 | 1.00 | 88 B | pass |
| Refuse a job document that is not a JSON object | POST /v1/foundry/jobs | 400 | 1.00 | 36 B | pass |
| Refuse a job body that is not JSON | POST /v1/foundry/jobs | 400 | 2.00 | 63 B | pass |
| Refuse a job with no recipe | POST /v1/foundry/jobs | 400 | 1.00 | 43 B | pass |
| Refuse a recipe name with capital letters | POST /v1/foundry/jobs | 400 | 1.00 | 49 B | pass |
| Refuse a job with no datasets | POST /v1/foundry/jobs | 400 | 2.00 | 56 B | pass |
| Refuse a job with an empty dataset list | POST /v1/foundry/jobs | 400 | 1.00 | 56 B | pass |
| Refuse a dataset id that is not a string | POST /v1/foundry/jobs | 400 | 1.00 | 32 B | pass |
| Refuse a budget of zero GPU-hours | POST /v1/foundry/jobs | 400 | 1.00 | 48 B | pass |
| Refuse a negative budget | POST /v1/foundry/jobs | 400 | 3.00 | 48 B | pass |
| Refuse a budget over the eight GPU-hour cap | POST /v1/foundry/jobs | 403 | 1.00 | 57 B | pass |
| Refuse an unknown job field | POST /v1/foundry/jobs | 400 | 1.00 | 42 B | pass |
| Refuse a job id with a space | POST /v1/foundry/jobs | 400 | 1.00 | 87 B | pass |
| Refuse a job id of forty-one characters | POST /v1/foundry/jobs | 400 | 1.00 | 87 B | pass |
| Refuse an unknown mode | POST /v1/foundry/jobs | 400 | 2.00 | 42 B | pass |
| Refuse an unknown target | POST /v1/foundry/jobs | 400 | 1.00 | 44 B | pass |
| Refuse an export format other than onnx | POST /v1/foundry/jobs | 400 | 1.00 | 30 B | pass |
| Refuse train overrides that are not an object | POST /v1/foundry/jobs | 400 | 1.00 | 43 B | pass |
| Refuse a device block with a string where a number goes | POST /v1/foundry/jobs | 400 | 1.00 | 63 B | pass |
| Refuse a recipe that does not exist | POST /v1/foundry/jobs | 404 | 1.00 | 38 B | pass |
| Refuse a dataset that does not exist | POST /v1/foundry/jobs | 404 | 1.00 | 35 B | pass |
| Refuse a plan that is not a JSON object | POST /v1/foundry/plan | 400 | 1.00 | 36 B | pass |
| Cancel a job that does not exist | POST /v1/foundry/jobs/tj-0000000000/cancel | 404 | 1.00 | 23 B | pass |
| Cancel a job id with a space in it | POST /v1/foundry/jobs/bad id/cancel | 404 | 1.00 | 23 B | pass |
| List the accelerators: every device is free, nothing is allocated | GET /v1/foundry/devices | 200 | 1.00 | 15 B | pass |
| Call the hub of a loose job that does not exist (init) | POST /v1/foundry/jobs/t-job-nohub/hub/init | 404 | 1.00 | 37 B | pass |
| Call the hub of a loose job that does not exist (state) | GET /v1/foundry/jobs/t-job-nohub/hub/state | 404 | 1.00 | 37 B | pass |
| Call the hub of a loose job that does not exist (round) | POST /v1/foundry/jobs/t-job-nohub/hub/round | 404 | 1.00 | 37 B | pass |
| Call the hub of a loose job that does not exist (health) | GET /v1/foundry/jobs/t-job-nohub/hub/health | 404 | 1.00 | 37 B | pass |
| List the models: none of ours yet | GET /v1/foundry/models | 200 | 2.00 | 13 B | pass |
| List the base models | GET /v1/foundry/models?kind=base | 200 | 2.00 | 13 B | pass |
| Refuse a model kind other than base | GET /v1/foundry/models?kind=other | 400 | 1.00 | 44 B | pass |
| Read a model that does not exist | GET /v1/foundry/models/t-no-model | 404 | 1.00 | 25 B | pass |
| Refuse a model import from a relative path | POST /v1/foundry/models/import | 400 | 0.00 | 28 B | pass |
| Refuse a model import from a directory that is not there | POST /v1/foundry/models/import | 400 | 2.00 | 75 B | pass |
| Refuse a model import with no path | POST /v1/foundry/models/import | 400 | 1.00 | 61 B | pass |
| Refuse a model import whose body is not JSON | POST /v1/foundry/models/import | 400 | 1.00 | 55 B | pass |
| Shadow a model that does not exist | POST /v1/foundry/models/t-no-model/shadow | 404 | 1.00 | 31 B | pass |
| Promote a model that does not exist | POST /v1/foundry/models/t-no-model/promote | 404 | 3.00 | 31 B | pass |
| Retire a model that does not exist | POST /v1/foundry/models/t-no-model/retire | 404 | 2.00 | 31 B | pass |
| Invoke a model that does not exist is refused and the error names it | POST /v1/foundry/models/t-no-model/invoke | 404 | 2.00 | 31 B | pass |
| Re-derive a model that does not exist | POST /v1/foundry/models/t-no-model/rederive | 404 | 1.00 | 25 B | pass |
| Register the shadow-promote model from its directory | POST /v1/foundry/models/import | 200 | 22.0 | 1.6 KiB | pass |
| Register the same model directory again | POST /v1/foundry/models/import | 400 | 1.00 | 75 B | pass |
| Invoke a trained model: only shadow and promoted models run | POST /v1/foundry/models/t-shadow-promote-314457fd/invoke | 400 | 1.00 | 91 B | pass |
| Promote a trained model: refused, it must be shadow first | POST /v1/foundry/models/t-shadow-promote-314457fd/promote | 409 | 2.00 | 81 B | pass |
| Move a trained model to shadow | POST /v1/foundry/models/t-shadow-promote-314457fd/shadow | 200 | 1.00 | 1.6 KiB | pass |
| Move a shadow model to shadow again: refused | POST /v1/foundry/models/t-shadow-promote-314457fd/shadow | 409 | 1.00 | 81 B | pass |
| Invoke a shadow model on a valid input | POST /v1/foundry/models/t-shadow-promote-314457fd/invoke | 200 | 2.00 | 129 B | pass |
| Invoke a shadow model with a feature missing | POST /v1/foundry/models/t-shadow-promote-314457fd/invoke | 400 | 2.00 | 46 B | pass |
| Invoke a shadow model with a feature that is a string | POST /v1/foundry/models/t-shadow-promote-314457fd/invoke | 400 | 1.00 | 46 B | pass |
| Promote a shadow model to a model agent | POST /v1/foundry/models/t-shadow-promote-314457fd/promote | 200 | 1.00 | 1.6 KiB | pass |
| Promote a promoted model again: refused | POST /v1/foundry/models/t-shadow-promote-314457fd/promote | 409 | 1.00 | 82 B | pass |
| Shadow a promoted model: refused | POST /v1/foundry/models/t-shadow-promote-314457fd/shadow | 409 | 1.00 | 83 B | pass |
| Invoke a promoted model | POST /v1/foundry/models/t-shadow-promote-314457fd/invoke | 200 | 1.00 | 129 B | pass |
| Read the promoted model with its agent cid | GET /v1/foundry/models/t-shadow-promote-314457fd | 200 | 2.00 | 1.6 KiB | pass |
| Retire the promoted model | POST /v1/foundry/models/t-shadow-promote-314457fd/retire | 200 | 1.00 | 1.6 KiB | pass |
| Retire a retired model again: refused | POST /v1/foundry/models/t-shadow-promote-314457fd/retire | 409 | 1.00 | 56 B | pass |
| Invoke a retired model: refused | POST /v1/foundry/models/t-shadow-promote-314457fd/invoke | 400 | 2.00 | 91 B | pass |
| Shadow a retired model: refused | POST /v1/foundry/models/t-shadow-promote-314457fd/shadow | 409 | 1.00 | 82 B | pass |
| Register the retire-trained model from its directory | POST /v1/foundry/models/import | 200 | 3.00 | 1.6 KiB | pass |
| Invoke a trained model: refused | POST /v1/foundry/models/t-retire-trained-314457fd/invoke | 400 | 2.00 | 91 B | pass |
| Promote a trained model: refused | POST /v1/foundry/models/t-retire-trained-314457fd/promote | 409 | 1.00 | 81 B | pass |
| Retire a trained model (the deny branch) | POST /v1/foundry/models/t-retire-trained-314457fd/retire | 200 | 1.00 | 1.6 KiB | pass |
| Move a retired model to shadow: refused | POST /v1/foundry/models/t-retire-trained-314457fd/shadow | 409 | 1.00 | 82 B | pass |
| Promote a retired model: refused | POST /v1/foundry/models/t-retire-trained-314457fd/promote | 409 | 1.00 | 81 B | pass |
| Retire a retired model again: refused | POST /v1/foundry/models/t-retire-trained-314457fd/retire | 409 | 1.00 | 56 B | pass |
| Read the retired model | GET /v1/foundry/models/t-retire-trained-314457fd | 200 | 1.00 | 1.6 KiB | pass |
| Read the Hugging Face status with no token | GET /v1/foundry/hf/status | 200 | 2.00 | 116 B | pass |
| Remove a token that was never set | DELETE /v1/foundry/hf/token | 200 | 1.00 | 22 B | pass |
| Refuse a token with a space in it | PUT /v1/foundry/hf/token | 400 | 1.00 | 53 B | pass |
| Refuse an empty token | PUT /v1/foundry/hf/token | 400 | 1.00 | 53 B | pass |
| Refuse a token with a line break in it | PUT /v1/foundry/hf/token | 400 | 2.00 | 53 B | pass |
| Refuse a token longer than 512 characters | PUT /v1/foundry/hf/token | 400 | 2.00 | 53 B | pass |
| Refuse a token that is not a string | PUT /v1/foundry/hf/token | 400 | 1.00 | 86 B | pass |
| Refuse a token body that is not JSON | PUT /v1/foundry/hf/token | 400 | 1.00 | 55 B | pass |
| The status still shows no token after the refusals | GET /v1/foundry/hf/status | 200 | 1.00 | 116 B | pass |
| Save a Hugging Face token (a fake test value) | PUT /v1/foundry/hf/token | 200 | 20.0 | 21 B | pass |
| The status shows a token is set | GET /v1/foundry/hf/status | 200 | 4.00 | 115 B | pass |
| Replace the saved token | PUT /v1/foundry/hf/token | 200 | 17.0 | 21 B | pass |
| Remove the saved token | DELETE /v1/foundry/hf/token | 200 | 17.0 | 22 B | pass |
| The status shows no token after removal | GET /v1/foundry/hf/status | 200 | 1.00 | 116 B | pass |
| Remove the token again: still no token | DELETE /v1/foundry/hf/token | 200 | 1.00 | 22 B | pass |
| Read the owner's fetch block list to restore it later | GET /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| Block huggingface.co for the owner's fetch (no network is ever used) | PUT /v1/settings | 200 | 2.00 | 1.2 KiB | pass |
| Refuse a search with a limit of zero | GET /v1/foundry/hf/models?search=t-llama&limit=0 | 400 | 2.00 | 44 B | pass |
| Refuse a search with a limit over the maximum | GET /v1/foundry/hf/models?search=t-llama&limit=101 | 400 | 1.00 | 46 B | pass |
| Refuse a search with a limit that is not a number | GET /v1/foundry/hf/models?search=t-llama&limit=ten | 400 | 1.00 | 46 B | pass |
| A search is refused while huggingface.co is blocked | GET /v1/foundry/hf/models?search=t-llama | 403 | 3.00 | 61 B | pass |
| Refuse a model id with three parts | GET /v1/foundry/hf/models/a/b/c | 400 | 1.00 | 70 B | pass |
| Refuse a revision with two dots | GET /v1/foundry/hf/models/t-org/t-model?revision=a..b | 400 | 1.00 | 38 B | pass |
| A model detail is refused while huggingface.co is blocked | GET /v1/foundry/hf/models/t-org/t-model | 403 | 13.0 | 106 B | pass |
| Refuse an import of a repo id with a space | POST /v1/foundry/hf/import | 400 | 3.00 | 72 B | pass |
| Refuse an import kind that is neither base nor servable | POST /v1/foundry/hf/import | 400 | 1.00 | 54 B | pass |
| Refuse an import whose file path climbs out of the repo | POST /v1/foundry/hf/import | 400 | 1.00 | 57 B | pass |
| Refuse an import of an absolute file path | POST /v1/foundry/hf/import | 400 | 1.00 | 55 B | pass |
| Refuse an import revision with two dots | POST /v1/foundry/hf/import | 400 | 1.00 | 38 B | pass |
| Refuse an import body that is not JSON | POST /v1/foundry/hf/import | 400 | 1.00 | 55 B | pass |
| A base import is refused while huggingface.co is blocked (nothing is downloaded) | POST /v1/foundry/hf/import | 403 | 55.0 | 106 B | pass |
| A servable import is refused while huggingface.co is blocked (nothing is downloaded) | POST /v1/foundry/hf/import | 403 | 18.0 | 106 B | pass |
| No Hugging Face import is listed after the refusals | GET /v1/foundry/hf/imports | 200 | 1.00 | 14 B | pass |
| Read an import number that does not exist | GET /v1/foundry/hf/imports/999 | 404 | 1.00 | 26 B | pass |
| Read an import number that is not a number | GET /v1/foundry/hf/imports/abc | 400 | 0.00 | 52 B | pass |
| Remove an import number that does not exist | DELETE /v1/foundry/hf/imports/999 | 404 | 1.00 | 26 B | pass |
| Remove an import number that is not a number | DELETE /v1/foundry/hf/imports/abc | 400 | 1.00 | 52 B | pass |
| Re-derive a model that does not exist (Hugging Face side) | POST /v1/foundry/models/t-no-model/rederive | 404 | 1.00 | 25 B | pass |
| A model detail with the slash encoded is refused while huggingface.co is blocked | GET /v1/foundry/hf/models/t-org/t-model | 403 | 21.0 | 106 B | pass |
| Restore the owner's fetch block list | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| List the datasets | GET /v1/foundry/datasets | 200 | 1.00 | 1.9 KiB | pass |
| List the jobs (empty on a fresh hub) | GET /v1/foundry/jobs | 200 | 1.00 | 2 B | pass |
| Get a job that does not exist | GET /v1/foundry/jobs/no-such-job | 404 | 1.00 | 23 B | pass |
| List the events of a job that does not exist | GET /v1/foundry/jobs/no-such-job/events?after=0 | 404 | 1.00 | 23 B | pass |
| Export a dataset from a body that is not JSON | POST /v1/foundry/datasets | 400 | 1.00 | 58 B | pass |
| Upload a dataset with no file named | POST /v1/foundry/datasets/upload | 400 | 1.00 | 41 B | pass |
| List the IDE's workspaces (only project repos on a fresh hub) | GET /v1/ide/workspaces | 200 | 2.00 | 713 B | pass |
| Read the checkpoints of a workspace that does not exist | GET /v1/ide/workspaces/repo:no-such-repo@main/checkpoints | 404 | 1.00 | 47 B | pass |
| Read the checkpoints of a malformed workspace id | GET /v1/ide/workspaces/not-a-workspace/checkpoints | 400 | 2.00 | 66 B | pass |
e2b
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Create a live E2B key for the later folders | POST /v1/e2b/keys | 200 | 28.0 | 130 B | pass |
| Create a key with a unicode label, trimmed | POST /v1/e2b/keys | 200 | 22.0 | 136 B | pass |
| Refuse a key label that is only spaces | POST /v1/e2b/keys | 400 | 1.00 | 88 B | pass |
| Refuse a key label of 81 characters | POST /v1/e2b/keys | 400 | 5.00 | 88 B | pass |
| List the keys without their secrets | GET /v1/e2b/keys | 200 | 1.00 | 172 B | pass |
| Accept an E2B key sent as a Bearer token | GET /e2b/sandboxes | 200 | 1.00 | 2 B | pass |
| Refuse an E2B key that was never issued | GET /e2b/sandboxes | 401 | 2.00 | 92 B | pass |
| Revoke the unicode key | DELETE /v1/e2b/keys/e2k-cf3e55deb192 | 200 | 10.0 | 11 B | pass |
| Refuse a revoked E2B key | GET /e2b/sandboxes | 401 | 2.00 | 92 B | pass |
| Refuse to revoke the same key twice | DELETE /v1/e2b/keys/e2k-cf3e55deb192 | 404 | 2.00 | 81 B | pass |
| The owner list no longer shows the revoked key | GET /v1/e2b/keys | 200 | 2.00 | 88 B | pass |
| Refuse a sandbox the node cannot start | POST /e2b/sandboxes | 503 | 2.00 | 148 B | pass |
| Refuse a v2 sandbox the node cannot start | POST /e2b/v2/sandboxes | 503 | 1.00 | 148 B | pass |
| Accept a Bearer key on create, then refuse for the node | POST /e2b/sandboxes | 503 | 1.00 | 148 B | pass |
| Refuse a create without an E2B key | POST /e2b/sandboxes | 401 | 1.00 | 92 B | pass |
| Refuse a create with a key that was never issued | POST /e2b/sandboxes | 401 | 0.00 | 92 B | pass |
| Accept the longest timeout a sandbox may have | POST /e2b/sandboxes | 503 | 1.00 | 148 B | pass |
| Refuse a timeout of zero | POST /e2b/sandboxes | 400 | 3.00 | 90 B | pass |
| Refuse a timeout over a day | POST /e2b/sandboxes | 400 | 1.00 | 90 B | pass |
| Refuse a timeout that is not a number | POST /e2b/sandboxes | 400 | 2.00 | 129 B | pass |
| Refuse a create without a template | POST /e2b/sandboxes | 400 | 2.00 | 121 B | pass |
| Refuse a blank template id | POST /e2b/sandboxes | 400 | 1.00 | 121 B | pass |
| Refuse a template id longer than 128 characters | POST /e2b/sandboxes | 400 | 1.00 | 121 B | pass |
| Refuse a create with a truncated body | POST /e2b/sandboxes | 400 | 1.00 | 111 B | pass |
| Refuse volume mounts on a sandbox | POST /e2b/sandboxes | 400 | 2.00 | 136 B | pass |
| Refuse network rules on a sandbox | POST /e2b/sandboxes | 400 | 2.00 | 143 B | pass |
| Refuse an egress proxy on a sandbox | POST /e2b/sandboxes | 400 | 2.00 | 140 B | pass |
| Refuse a masked request host on a sandbox | POST /e2b/sandboxes | 400 | 1.00 | 93 B | pass |
| Refuse an allowOut entry that is not a host | POST /e2b/sandboxes | 400 | 1.00 | 137 B | pass |
| Refuse a denyOut entry that names a host | POST /e2b/sandboxes | 400 | 1.00 | 128 B | pass |
| Refuse a denyOut block with a prefix over 32 | POST /e2b/sandboxes | 400 | 1.00 | 128 B | pass |
| Refuse a metadata key that holds an equals sign | POST /e2b/sandboxes | 400 | 4.00 | 90 B | pass |
| Refuse a metadata value over 1024 characters | POST /e2b/sandboxes | 400 | 1.00 | 104 B | pass |
| Refuse a metadata key with a control character | POST /e2b/sandboxes | 400 | 1.00 | 126 B | pass |
| Accept an egress policy with no internet, then refuse for the node | POST /e2b/v2/sandboxes | 503 | 1.00 | 148 B | pass |
| Refuse an unknown sandbox state in the list | GET /e2b/v2/sandboxes?state=frozen | 400 | 1.00 | 80 B | pass |
| List running and paused sandboxes by a filter no sandbox has | GET /e2b/v2/sandboxes?state=running,paused&metadata=e2bnone%3Dyes | 200 | 1.00 | 2 B | pass |
| Refuse a metadata filter that is not key=value | GET /e2b/sandboxes?metadata=novalue | 400 | 2.00 | 100 B | pass |
| List the running sandboxes by a filter no sandbox has | GET /e2b/sandboxes?metadata=e2bnone%3Dyes | 200 | 1.00 | 2 B | pass |
| List the snapshots | GET /e2b/snapshots | 200 | 1.00 | 2 B | pass |
| Refuse the metrics of several sandboxes without ids (sandbox_ids is required) | GET /e2b/sandboxes/metrics | 400 | 1.00 | 79 B | pass |
| Answer the metrics of unknown sandboxes with an empty map | GET /e2b/sandboxes/metrics?sandbox_ids=sbe2bnone1,sbe2bnone2 | 200 | 1.00 | 16 B | pass |
| Refuse the metrics of more than 100 sandboxes | GET /e2b/sandboxes/metrics?sandbox_ids=sbe2bmany000,sbe2bmany001,sbe2bmany002,sbe2bmany003,sbe2bmany004,sbe2bmany005,sbe2bmany006,sbe2bmany007,sbe2bmany008,sbe2bmany009,sbe2bmany010,sbe2bmany011,sbe2bmany012,sbe2bmany013,sbe2bmany014,sbe2bmany015,sbe2bmany016,sbe2bmany017,sbe2bmany018,sbe2bmany019,sbe2bmany020,sbe2bmany021,sbe2bmany022,sbe2bmany023,sbe2bmany024,sbe2bmany025,sbe2bmany026,sbe2bmany027,sbe2bmany028,sbe2bmany029,sbe2bmany030,sbe2bmany031,sbe2bmany032,sbe2bmany033,sbe2bmany034,sbe2bmany035,sbe2bmany036,sbe2bmany037,sbe2bmany038,sbe2bmany039,sbe2bmany040,sbe2bmany041,sbe2bmany042,sbe2bmany043,sbe2bmany044,sbe2bmany045,sbe2bmany046,sbe2bmany047,sbe2bmany048,sbe2bmany049,sbe2bmany050,sbe2bmany051,sbe2bmany052,sbe2bmany053,sbe2bmany054,sbe2bmany055,sbe2bmany056,sbe2bmany057,sbe2bmany058,sbe2bmany059,sbe2bmany060,sbe2bmany061,sbe2bmany062,sbe2bmany063,sbe2bmany064,sbe2bmany065,sbe2bmany066,sbe2bmany067,sbe2bmany068,sbe2bmany069,sbe2bmany070,sbe2bmany071,sbe2bmany072,sbe2bmany073,sbe2bmany074,sbe2bmany075,sbe2bmany076,sbe2bmany077,sbe2bmany078,sbe2bmany079,sbe2bmany080,sbe2bmany081,sbe2bmany082,sbe2bmany083,sbe2bmany084,sbe2bmany085,sbe2bmany086,sbe2bmany087,sbe2bmany088,sbe2bmany089,sbe2bmany090,sbe2bmany091,sbe2bmany092,sbe2bmany093,sbe2bmany094,sbe2bmany095,sbe2bmany096,sbe2bmany097,sbe2bmany098,sbe2bmany099,sbe2bmany100 | 400 | 1.00 | 79 B | pass |
| Refuse to end a sandbox that does not exist | DELETE /e2b/sandboxes/sbffffffffffffffff | 404 | 2.00 | 87 B | pass |
| Refuse to set the end of a sandbox that does not exist | POST /e2b/sandboxes/sbffffffffffffffff/timeout | 404 | 1.00 | 87 B | pass |
| Refuse a timeout without a value | POST /e2b/sandboxes/sbffffffffffffffff/timeout | 400 | 3.00 | 75 B | pass |
| Refuse a negative timeout | POST /e2b/sandboxes/sbffffffffffffffff/timeout | 400 | 2.00 | 90 B | pass |
| Refuse to extend a sandbox that does not exist | POST /e2b/sandboxes/sbffffffffffffffff/refreshes | 404 | 1.00 | 87 B | pass |
| Refuse a refresh longer than an hour | POST /e2b/sandboxes/sbffffffffffffffff/refreshes | 400 | 1.00 | 90 B | pass |
| Refuse to pause a sandbox that does not exist | POST /e2b/sandboxes/sbffffffffffffffff/pause | 404 | 2.00 | 87 B | pass |
| Refuse to resume a sandbox that does not exist | POST /e2b/sandboxes/sbffffffffffffffff/resume | 404 | 1.00 | 87 B | pass |
| Refuse a v1 connect to a sandbox that does not exist | POST /e2b/sandboxes/sbffffffffffffffff/connect | 404 | 2.00 | 87 B | pass |
| Refuse a v1 connect without a timeout | POST /e2b/sandboxes/sbffffffffffffffff/connect | 400 | 1.00 | 75 B | pass |
| Refuse a v2 connect to a sandbox that does not exist | POST /e2b/v2/sandboxes/sbffffffffffffffff/connect | 404 | 1.00 | 87 B | pass |
| Refuse the metrics of a sandbox that does not exist | GET /e2b/sandboxes/sbffffffffffffffff/metrics | 404 | 2.00 | 87 B | pass |
| Refuse the v2 logs of a sandbox that does not exist | GET /e2b/v2/sandboxes/sbffffffffffffffff/logs?limit=10 | 404 | 1.00 | 87 B | pass |
| Refuse the v1 logs of a sandbox that does not exist | GET /e2b/sandboxes/sbffffffffffffffff/logs?start=0 | 404 | 1.00 | 87 B | pass |
| Refuse to replace the network rules of a sandbox that does not exist | PUT /e2b/sandboxes/sbffffffffffffffff/network | 404 | 1.00 | 87 B | pass |
| Refuse a network rule that is not an address on deny | PUT /e2b/sandboxes/sbffffffffffffffff/network | 400 | 3.00 | 126 B | pass |
| Refuse a snapshot of a sandbox that does not exist | POST /e2b/sandboxes/sbffffffffffffffff/snapshots | 404 | 1.00 | 87 B | pass |
| Refuse a snapshot name with a control character | POST /e2b/sandboxes/sbffffffffffffffff/snapshots | 400 | 2.00 | 97 B | pass |
| Refuse a fork of a sandbox that does not exist | POST /e2b/sandboxes/sbffffffffffffffff/fork | 404 | 1.00 | 87 B | pass |
| Refuse a fork of nine sandboxes | POST /e2b/sandboxes/sbffffffffffffffff/fork | 400 | 1.00 | 76 B | pass |
| Refuse a fork of no sandbox | POST /e2b/sandboxes/sbffffffffffffffff/fork | 400 | 1.00 | 76 B | pass |
| Refuse to delete a template that does not exist | DELETE /e2b/templates/e2b-nope-tmpl | 404 | 1.00 | 84 B | pass |
| Refuse the owner's end of a sandbox that does not exist | DELETE /v1/e2b/sandboxes/sbffffffffffffffff | 404 | 1.00 | 87 B | pass |
| Refuse the owner's pause of a sandbox that does not exist | POST /v1/e2b/sandboxes/sbffffffffffffffff/pause | 404 | 1.00 | 87 B | pass |
| Refuse the owner's resume of a sandbox that does not exist | POST /v1/e2b/sandboxes/sbffffffffffffffff/resume | 404 | 1.00 | 87 B | pass |
| Refuse a sandbox id that is not an id at all | GET /e2b/sandboxes/SB-not-an-id | 404 | 1.00 | 81 B | pass |
| Request a build for the held checks | POST /e2b/v3/templates | 202 | 45.0 | 174 B | pass |
| Hold the plaza with STOP ALL | POST /v1/plaza/stop | 200 | 2.00 | 100 B | pass |
| Refuse a sandbox while the plaza is held | POST /e2b/sandboxes | 409 | 1.00 | 118 B | pass |
| Refuse a v2 sandbox while the plaza is held | POST /e2b/v2/sandboxes | 409 | 2.00 | 118 B | pass |
| Refuse a fork while held, before the sandbox is looked up | POST /e2b/sandboxes/sbffffffffffffffff/fork | 409 | 1.00 | 118 B | pass |
| Refuse a build start while the plaza is held | POST /e2b/v2/templates/5dfaa8aa3688b587141f/builds/b2d60ebe-3451-42f2-9f91-75ae5815ad66 | 409 | 1.00 | 118 B | pass |
| Leave the build waiting after a refused start | GET /e2b/templates/5dfaa8aa3688b587141f/builds/b2d60ebe-3451-42f2-9f91-75ae5815ad66/status | 200 | 0.00 | 339 B | pass |
| Release the STOP ALL hold | POST /v1/plaza/resume | 200 | 1.00 | 80 B | pass |
| Reach the node again after the release | POST /e2b/sandboxes | 503 | 1.00 | 148 B | pass |
| Start the build after the release | POST /e2b/v2/templates/5dfaa8aa3688b587141f/builds/b2d60ebe-3451-42f2-9f91-75ae5815ad66 | 202 | 46.0 | 2 B | pass |
| Read the held build as failed at its base | GET /e2b/templates/5dfaa8aa3688b587141f/builds/b2d60ebe-3451-42f2-9f91-75ae5815ad66/status | 200 | 2.00 | 907 B | pass |
| Delete the held template | DELETE /e2b/templates/5dfaa8aa3688b587141f | 204 | 26.0 | 0 B | pass |
| Request a build of a new template | POST /e2b/v3/templates | 202 | 7.00 | 171 B | pass |
| Read the new build as waiting | GET /e2b/templates/441a772d82c84fbd9e3a/builds/494a64f1-7774-4680-8a26-a95a58980442/status | 200 | 2.00 | 339 B | pass |
| Find the template by its alias | GET /e2b/templates/aliases/e2b-fail-tmpl | 200 | 1.00 | 52 B | pass |
| Refuse an alias no template has | GET /e2b/templates/aliases/e2b-no-such-tmpl | 404 | 1.00 | 87 B | pass |
| Show the waiting build in the owner's list | GET /v1/e2b/templates | 200 | 3.00 | 344 B | pass |
| Ask for an upload link for the archive | GET /e2b/templates/441a772d82c84fbd9e3a/files/abababababababababababababababababababababababababababababababab | 201 | 1.00 | 250 B | pass |
| Refuse an upload link for a file hash that is not hex | GET /e2b/templates/441a772d82c84fbd9e3a/files/not-a-hash | 400 | 1.00 | 85 B | pass |
| Refuse an upload without its link token | PUT /e2b/templates/441a772d82c84fbd9e3a/files/abababababababababababababababababababababababababababababababab | 403 | 2.00 | 106 B | pass |
| Upload the archive with its link token | PUT /e2b/templates/441a772d82c84fbd9e3a/files/abababababababababababababababababababababababababababababababab | 200 | 14.0 | 2 B | pass |
| Refuse a second upload on a spent link | PUT /e2b/templates/441a772d82c84fbd9e3a/files/abababababababababababababababababababababababababababababababab | 403 | 0.00 | 106 B | pass |
| Show the archive as present once uploaded | GET /e2b/templates/441a772d82c84fbd9e3a/files/abababababababababababababababababababababababababababababababab | 201 | 1.00 | 249 B | pass |
| Start the build with a copied archive and a command | POST /e2b/v2/templates/441a772d82c84fbd9e3a/builds/494a64f1-7774-4680-8a26-a95a58980442 | 202 | 20.0 | 2 B | pass |
| Refuse a second start of the same build | POST /e2b/v2/templates/441a772d82c84fbd9e3a/builds/494a64f1-7774-4680-8a26-a95a58980442 | 409 | 1.00 | 122 B | pass |
| Read the build while it runs or has failed | GET /e2b/templates/441a772d82c84fbd9e3a/builds/494a64f1-7774-4680-8a26-a95a58980442/status | 200 | 1.00 | 907 B | pass |
| Read the build as failed at its base | GET /e2b/templates/441a772d82c84fbd9e3a/builds/494a64f1-7774-4680-8a26-a95a58980442/status | 200 | 1.00 | 907 B | pass |
| Refuse a tag on a build that failed | POST /e2b/templates/tags | 404 | 1.00 | 104 B | pass |
| Refuse a start of the failed build | POST /e2b/v2/templates/441a772d82c84fbd9e3a/builds/494a64f1-7774-4680-8a26-a95a58980442 | 409 | 1.00 | 122 B | pass |
| Show the failed build and its reason in the owner's list | GET /v1/e2b/templates | 200 | 3.00 | 471 B | pass |
| Delete the failed template | DELETE /e2b/templates/441a772d82c84fbd9e3a | 204 | 117 | 0 B | pass |
| Refuse a second delete of the template | DELETE /e2b/templates/441a772d82c84fbd9e3a | 404 | 2.00 | 91 B | pass |
| The alias is gone with the template | GET /e2b/templates/aliases/e2b-fail-tmpl | 404 | 1.00 | 84 B | pass |
| The build's status is gone with the template | GET /e2b/templates/441a772d82c84fbd9e3a/builds/494a64f1-7774-4680-8a26-a95a58980442/status | 404 | 1.00 | 91 B | pass |
| The owner's list no longer has the template | GET /v1/e2b/templates | 200 | 1.00 | 16 B | pass |
| Request a build named with a tag, and more tags | POST /e2b/v3/templates | 202 | 21.0 | 180 B | pass |
| Request a second build of the same template | POST /e2b/v3/templates | 202 | 9.00 | 174 B | pass |
| The owner sees both waiting builds | GET /v1/e2b/templates | 200 | 1.00 | 523 B | pass |
| Refuse a tag on a build that is not ready | POST /e2b/templates/tags | 404 | 2.00 | 104 B | pass |
| Refuse a tag on a template that does not exist | POST /e2b/templates/tags | 404 | 1.00 | 84 B | pass |
| Refuse a tag with a space in it | POST /e2b/templates/tags | 400 | 1.00 | 116 B | pass |
| Refuse an empty list of tags | POST /e2b/templates/tags | 400 | 1.00 | 73 B | pass |
| Refuse more than 32 tags | POST /e2b/templates/tags | 400 | 4.00 | 73 B | pass |
| Remove tags no build holds | DELETE /e2b/templates/tags | 204 | 1.00 | 0 B | pass |
| Refuse removing tags from a template that does not exist | DELETE /e2b/templates/tags | 404 | 1.00 | 84 B | pass |
| List a template's tags by its name | GET /e2b/templates/e2b-wait-tmpl/tags | 200 | 1.00 | 2 B | pass |
| Refuse the tags of a template that does not exist | GET /e2b/templates/e2b-nope-tmpl/tags | 404 | 1.00 | 84 B | pass |
| Delete the waiting template from the owner console | DELETE /v1/e2b/templates/66fb2ad1b09e18e4cd0c | 204 | 23.0 | 0 B | pass |
| Refuse a second owner delete of the template | DELETE /v1/e2b/templates/66fb2ad1b09e18e4cd0c | 404 | 1.00 | 91 B | pass |
| The alias is gone | GET /e2b/templates/aliases/e2b-wait-tmpl | 404 | 1.00 | 84 B | pass |
| Refuse the status of a deleted build | GET /e2b/templates/66fb2ad1b09e18e4cd0c/builds/306d25be-6398-4e4f-ac2e-fcd8ed2c7230/status | 404 | 1.00 | 91 B | pass |
| Refuse a start of a deleted build | POST /e2b/v2/templates/66fb2ad1b09e18e4cd0c/builds/306d25be-6398-4e4f-ac2e-fcd8ed2c7230 | 404 | 2.00 | 91 B | pass |
| Request a build for the refused starts | POST /e2b/v3/templates | 202 | 13.0 | 180 B | pass |
| Refuse a start with a malformed body | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 1.00 | 111 B | pass |
| Refuse a start with no base image or template | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 2.00 | 86 B | pass |
| Refuse another base image | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 0.00 | 181 B | pass |
| Refuse a registry image source | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 1.00 | 138 B | pass |
| Refuse a source template that does not exist | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 1.00 | 103 B | pass |
| Refuse a RUN step with no command | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 1.00 | 80 B | pass |
| Refuse a COPY whose files were never uploaded | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 2.00 | 163 B | pass |
| Refuse a step that is not a build step | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 1.00 | 119 B | pass |
| Refuse an ENV step with an odd number of arguments | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 1.00 | 100 B | pass |
| Refuse an ENV step whose name is not a variable name | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 1.00 | 99 B | pass |
| Refuse a USER step for a user the sandbox lacks | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 2.00 | 117 B | pass |
| Refuse a start command longer than 64 KiB | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 400 | 1.00 | 106 B | pass |
| Refuse a start of a build that does not exist | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/00000000-0000-4000-8000-000000000000 | 404 | 2.00 | 110 B | pass |
| Refuse a start of a template that does not exist | POST /e2b/v2/templates/e2b-nope-tmpl/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 404 | 1.00 | 84 B | pass |
| Refuse a start without an E2B key | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 401 | 1.00 | 92 B | pass |
| Leave the build waiting after all the refusals | GET /e2b/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a/status | 200 | 1.00 | 345 B | pass |
| Start the build after the refusals | POST /e2b/v2/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a | 202 | 32.0 | 2 B | pass |
| Read the build as failed at its base | GET /e2b/templates/2f4cae852e4745175b6a/builds/9faf64fc-58b9-412e-bd8b-839625c08e2a/status | 200 | 1.00 | 913 B | pass |
| Delete the template by its name | DELETE /e2b/templates/e2b-refused-tmpl | 204 | 28.0 | 0 B | pass |
| Refuse a template named base | POST /e2b/v3/templates | 400 | 2.00 | 89 B | pass |
| Refuse a template named aliases | POST /e2b/v3/templates | 400 | 1.00 | 91 B | pass |
| Refuse a template named tags | POST /e2b/v3/templates | 400 | 1.00 | 88 B | pass |
| Refuse a template name with a space | POST /e2b/v3/templates | 400 | 1.00 | 165 B | pass |
| Refuse a template name that starts with a dash | POST /e2b/v3/templates | 400 | 1.00 | 162 B | pass |
| Refuse a template name longer than 128 characters | POST /e2b/v3/templates | 400 | 4.00 | 286 B | pass |
| Refuse a template name with accents | POST /e2b/v3/templates | 400 | 1.00 | 164 B | pass |
| Refuse an empty template name | POST /e2b/v3/templates | 400 | 1.00 | 157 B | pass |
| Refuse a tag with a space in it | POST /e2b/v3/templates | 400 | 1.00 | 116 B | pass |
| Refuse more than 32 tags | POST /e2b/v3/templates | 400 | 1.00 | 71 B | pass |
| Refuse a CPU count of zero | POST /e2b/v3/templates | 400 | 1.00 | 73 B | pass |
| Refuse a CPU count of nine | POST /e2b/v3/templates | 400 | 1.00 | 73 B | pass |
| Refuse a memory size of 127 MiB | POST /e2b/v3/templates | 400 | 1.00 | 81 B | pass |
| Refuse a memory size of 16385 MiB | POST /e2b/v3/templates | 400 | 1.00 | 83 B | pass |
| Refuse a build request with no body | POST /e2b/v3/templates | 400 | 1.00 | 110 B | pass |
| Refuse a malformed build request | POST /e2b/v3/templates | 400 | 2.00 | 110 B | pass |
| Refuse a build request without an E2B key | POST /e2b/v3/templates | 401 | 1.00 | 92 B | pass |
| Accept the largest VM a build may ask for | POST /e2b/v3/templates | 202 | 17.0 | 172 B | pass |
| Delete the largest-VM template as the owner | DELETE /v1/e2b/templates/ccc48e08e0cb5e416ef7 | 204 | 8.00 | 0 B | pass |
| Revoke the live key at the end | DELETE /v1/e2b/keys/e2k-1e03ab3607f6 | 200 | 9.00 | 11 B | pass |
| Refuse the revoked live key | GET /e2b/sandboxes | 401 | 1.00 | 92 B | pass |
| Create an E2B key for the reads | POST /v1/e2b/keys | 200 | 24.0 | 131 B | pass |
| List the running sandboxes (empty) | GET /e2b/sandboxes | 200 | 1.00 | 2 B | pass |
| List the sandboxes by state (empty) | GET /e2b/v2/sandboxes?state=running,paused | 200 | 0.00 | 2 B | pass |
| List every sandbox for the owner's console (empty) | GET /v1/e2b/sandboxes | 200 | 1.00 | 16 B | pass |
| Get a sandbox that does not exist | GET /e2b/sandboxes/sb0000000000000000 | 404 | 0.00 | 87 B | pass |
| Refuse the sandbox list without an E2B key | GET /e2b/sandboxes | 401 | 1.00 | 92 B | pass |
| List the templates and their builds (empty) | GET /v1/e2b/templates | 200 | 0.00 | 16 B | pass |
| Revoke the E2B key | DELETE /v1/e2b/keys/e2k-7b02876ad5bc | 200 | 14.0 | 11 B | pass |
evals
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| The cost and success report by step | GET /v1/evals/costs?by=step | 200 | 2.00 | 443 B | pass |
| A cost report refuses a grouping it does not know | GET /v1/evals/costs?by=seat | 400 | 4.00 | 65 B | pass |
| The leaderboard of the build step | GET /v1/evals/leaderboard?step=build | 200 | 1.00 | 242 B | pass |
| Each reviewer model's precision and misses | GET /v1/evals/reviewers | 200 | 1.00 | 2 B | pass |
| The benchmark's replays of archived tasks | GET /v1/evals/bench | 200 | 1.00 | 2 B | pass |
| Archive a task with its known outcome as a benchmark case | POST /v1/evals/archive | 201 | 29.0 | 8 B | pass |
| Replay an archived case as a task in its project | POST /v1/evals/bench/1 | 201 | 108 | 60 B | pass |
| Record a task's qualification stages and lab minutes | POST /v1/evals/qualified | 200 | 8.00 | 37 B | pass |
weft
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Create a Weft repo as the owner, through the console | POST /v1/weft/console | 200 | 8.00 | 48 B | pass |
| The new repo is in the list of repos, with its project | GET /v1/weft/repos | 200 | 1.00 | 945 B | pass |
| Read the new repo by its name | GET /v1/weft/repos/t-weft-repo-a | 200 | 1.00 | 181 B | pass |
| Refuse a second repo with the same name | POST /v1/weft/console | 409 | 2.00 | 37 B | pass |
| Refuse a repo name with a space in it | POST /v1/weft/console | 400 | 2.00 | 36 B | pass |
| Refuse a repo name with non-ASCII letters | POST /v1/weft/console | 400 | 1.00 | 45 B | pass |
| Refuse a console statement with no kind | POST /v1/weft/console | 400 | 1.00 | 28 B | pass |
| Refuse a console body that is not JSON | POST /v1/weft/console | 400 | 5.00 | 55 B | pass |
| Answer 404 for a repo that does not exist | GET /v1/weft/repos/t-weft-no-such-repo | 404 | 1.00 | 24 B | pass |
| Create the repo a connection will be added to | POST /v1/weft/console | 200 | 23.0 | 49 B | pass |
| Add a connection from the repo to a remote, as the owner | POST /v1/weft/console | 200 | 12.0 | 87 B | pass |
| The repo shows its connection as in step, with no credential in it | GET /v1/weft/repos/t-weft-repo-b | 200 | 2.00 | 383 B | pass |
| Refuse a second connection with the same id on the repo | POST /v1/weft/console | 409 | 1.00 | 59 B | pass |
| Refuse a connection whose exported commits have no author email | POST /v1/weft/console | 400 | 1.00 | 49 B | pass |
| Refuse a connection to a repo that does not exist | POST /v1/weft/console | 400 | 1.00 | 39 B | pass |
| Remove the connection from the repo, as the owner | POST /v1/weft/console | 200 | 8.00 | 58 B | pass |
| After the removal the repo lists no connection | GET /v1/weft/repos/t-weft-repo-b | 200 | 1.00 | 173 B | pass |
| Refuse removing a connection that is already gone | POST /v1/weft/console | 400 | 1.00 | 34 B | pass |
| Create the repo a change will be opened in | POST /v1/weft/console | 200 | 52.0 | 49 B | pass |
| Open a lane on the repo's main for a task, as the owner | POST /v1/weft/lanes | 200 | 57.0 | 398 B | pass |
| Read the open change by its name | GET /v1/weft/changes/w-1 | 200 | 2.00 | 372 B | pass |
| Refuse a lane on a repo that does not exist | POST /v1/weft/lanes | 400 | 2.00 | 39 B | pass |
| Refuse a lane whose author is not a known actor | POST /v1/weft/lanes | 400 | 2.00 | 85 B | pass |
| Refuse a release proposal on a repo with nothing landed | POST /v1/weft/repos/t-weft-repo-d/releases | 400 | 2.00 | 41 B | pass |
| Answer 404 for a change that does not exist | GET /v1/weft/changes/w-999999 | 404 | 1.00 | 30 B | pass |
| Upload an object whose bytes hash to its name | PUT /v1/weft/wire/objects/af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262 | 200 | 39.0 | 77 B | pass |
| Read the uploaded object back by its hash | GET /v1/weft/wire/objects/af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262 | 200 | 1.00 | 0 B | pass |
| Refuse an upload whose bytes do not hash to the name given | PUT /v1/weft/wire/objects/0000000000000000000000000000000000000000000000000000000000000000 | 400 | 1.00 | 60 B | pass |
| Refuse an upload whose name is not a hash | PUT /v1/weft/wire/objects/not-a-hash | 400 | 1.00 | 22 B | pass |
| Refuse a statement that is not signed | POST /v1/weft/records | 400 | 1.00 | 24 B | pass |
| Answer 404 for an object no one stored | GET /v1/weft/wire/objects/1111111111111111111111111111111111111111111111111111111111111111 | 404 | 1.00 | 32 B | pass |
| List the review recipes, with the three built in at their versions | GET /v1/recipes | 200 | 6.00 | 1.8 KiB | pass |
| Show the built-in docs recipe, with its TOML | GET /v1/recipes/docs | 200 | 2.00 | 2.6 KiB | pass |
| Show the docs recipe at the version named | GET /v1/recipes/docs?version=1 | 200 | 3.00 | 2.6 KiB | pass |
| Answer 404 for a recipe version that was never saved | GET /v1/recipes/docs?version=9 | 404 | 2.00 | 26 B | pass |
| Answer 404 for a recipe that does not exist | GET /v1/recipes/t-no-such-recipe | 404 | 2.00 | 26 B | pass |
| Refuse saving docs at version 1, which the built-in already holds | PUT /v1/recipes/docs | 400 | 2.00 | 49 B | pass |
| Refuse a recipe saved under another recipe's id | PUT /v1/recipes/research | 400 | 2.00 | 46 B | pass |
| Refuse recipe TOML that does not parse | PUT /v1/recipes/docs | 400 | 2.00 | 152 B | pass |
| Refuse a recipe with two gates | PUT /v1/recipes/docs | 400 | 1.00 | 41 B | pass |
| Refuse a recipe whose nodes wait on each other | PUT /v1/recipes/docs | 400 | 1.00 | 43 B | pass |
| Refuse a recipe node of a kind the graph does not know | PUT /v1/recipes/docs | 400 | 1.00 | 37 B | pass |
| Refuse a recipe save with no TOML in it | PUT /v1/recipes/docs | 400 | 2.00 | 30 B | pass |
| Answer 409 when deciding a recipe proposal that is not pending | POST /v1/recipes/proposals/rp-999 | 409 | 0.00 | 38 B | pass |
| Save the owner's docs recipe as version 2 | PUT /v1/recipes/docs | 200 | 15.0 | 649 B | pass |
| The latest docs recipe is the owner's version 2 | GET /v1/recipes/docs | 200 | 3.00 | 1.3 KiB | pass |
| The built-in docs version 1 still reads as built in | GET /v1/recipes/docs?version=1 | 200 | 1.00 | 2.6 KiB | pass |
| The recipe list keeps both docs versions | GET /v1/recipes | 200 | 6.00 | 2.0 KiB | pass |
| Refuse saving docs at version 2 a second time | PUT /v1/recipes/docs | 400 | 2.00 | 49 B | pass |
| Revert docs to the built-in content, saved as a newer version 3 | PUT /v1/recipes/docs | 200 | 17.0 | 1.2 KiB | pass |
| The latest docs recipe is the reverted version 3, with the built-in title | GET /v1/recipes/docs | 200 | 1.00 | 2.6 KiB | pass |
| The owner's version 2 is kept in the history after the revert | GET /v1/recipes/docs?version=2 | 200 | 1.00 | 1.3 KiB | pass |
| Refuse saving the code recipe at version 3, which the built-in holds | PUT /v1/recipes/code | 400 | 2.00 | 49 B | pass |
| Show whether Weft is running on a fresh hub | GET /v1/weft | 200 | 0.00 | 46 B | pass |
| List the Weft repositories on a fresh hub | GET /v1/weft/repos | 200 | 1.00 | 1.3 KiB | pass |
| List the language packs Weft has loaded | GET /v1/weft/packs | 200 | 1.00 | 318 B | pass |
| List the Weft keys, with the owner's on a fresh hub | GET /v1/weft/keys | 200 | 2.00 | 634 B | pass |
| List the Weft operations log, newest first | GET /v1/weft/ops?limit=20 | 200 | 2.00 | 2.2 KiB | pass |
| List the review recipes and the proposals waiting | GET /v1/recipes | 200 | 6.00 | 2.1 KiB | pass |
| Show the code review recipe as its TOML | GET /v1/recipes/code | 200 | 1.00 | 6.7 KiB | pass |
platforms
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Record a GitLab OAuth app, its secret kept out of the answer | POST /v1/platforms/registrations | 200 | 92.0 | 136 B | pass |
| List the registered app with no secret in the list | GET /v1/platforms | 200 | 1.00 | 459 B | pass |
| Refuse an OAuth app with no client secret | POST /v1/platforms/registrations | 400 | 1.00 | 37 B | pass |
| Refuse an OAuth app whose client id is blank | POST /v1/platforms/registrations | 400 | 1.00 | 33 B | pass |
| Refuse an OAuth app on a platform the plaza does not know | POST /v1/platforms/registrations | 400 | 1.00 | 45 B | pass |
| Refuse a platform host that is not an http(s) URL | POST /v1/platforms/registrations | 400 | 1.00 | 72 B | pass |
| Forget the registered app and its secret | DELETE /v1/platforms/registrations/gitlab-app-1 | 200 | 31.0 | 26 B | pass |
| Answer 404 when forgetting an app already forgotten | DELETE /v1/platforms/registrations/gitlab-app-1 | 404 | 1.00 | 23 B | pass |
| Refuse a sign-in with an app that was forgotten | POST /v1/platforms/authorize | 404 | 3.00 | 23 B | pass |
| The forgotten app is gone from the list | GET /v1/platforms | 200 | 0.00 | 323 B | pass |
| Record a Forgejo OAuth app for a sign-in the platform declines | POST /v1/platforms/registrations | 200 | 22.0 | 147 B | pass |
| Start the owner's sign-in on the registered app | POST /v1/platforms/authorize | 200 | 0.00 | 129 B | pass |
| Show the started sign-in as pending | GET /v1/platforms/flows/f88f356f952e3ad38acf9eb24bb4fba2 | 200 | 1.00 | 20 B | pass |
| Open the go page, which redirects to the platform's authorize page | GET /v1/platforms/flows/f88f356f952e3ad38acf9eb24bb4fba2/go | 302 | 1.00 | 0 B | pass |
| Take the browser back with the platform's refusal | GET /v1/platforms/callback?error=access_denied&state=f88f356f952e3ad38acf9eb24bb4fba2 | 400 | 1.00 | 484 B | pass |
| Show the declined sign-in as failed, with the platform's reason | GET /v1/platforms/flows/f88f356f952e3ad38acf9eb24bb4fba2 | 200 | 0.00 | 85 B | pass |
| Refuse a callback that replays a state already used | GET /v1/platforms/callback?code=t-replayed-code&state=f88f356f952e3ad38acf9eb24bb4fba2 | 400 | 2.00 | 501 B | pass |
| Forget the app the sign-in was declined on | DELETE /v1/platforms/registrations/forgejo-app-2 | 200 | 94.0 | 27 B | pass |
| Record a GitLab OAuth app whose token exchange will be refused | POST /v1/platforms/registrations | 200 | 61.0 | 145 B | pass |
| Start the sign-in on the app whose exchange will fail | POST /v1/platforms/authorize | 200 | 0.00 | 129 B | pass |
| Take the browser back with a code, and the exchange is refused by the host | GET /v1/platforms/callback?code=t-platforms-code&state=ef3c8113dff994fc5e749b8f31445346 | 400 | 1.00 | 509 B | pass |
| Show the failed exchange as failed, without the code | GET /v1/platforms/flows/ef3c8113dff994fc5e749b8f31445346 | 200 | 1.00 | 110 B | pass |
| Forget the app whose exchange failed | DELETE /v1/platforms/registrations/gitlab-app-3 | 200 | 41.0 | 26 B | pass |
| Start creating the plaza's GitHub App on a host that refuses | POST /v1/platforms/github/app | 200 | 1.00 | 129 B | pass |
| Refuse a GitHub host that is not an http(s) URL | POST /v1/platforms/github/app | 400 | 1.00 | 72 B | pass |
| Show the app creation as pending | GET /v1/platforms/flows/41f9c83bdb9f30bd0d585059fe3714b2 | 200 | 2.00 | 20 B | pass |
| Open the go page, which carries the manifest form for the host | GET /v1/platforms/flows/41f9c83bdb9f30bd0d585059fe3714b2/go | 200 | 1.00 | 1.4 KiB | pass |
| Take the browser back with a code the host refuses to convert | GET /v1/platforms/callback?code=t-manifest-code&state=41f9c83bdb9f30bd0d585059fe3714b2 | 400 | 2.00 | 546 B | pass |
| Show the failed app creation as failed | GET /v1/platforms/flows/41f9c83bdb9f30bd0d585059fe3714b2 | 200 | 1.00 | 147 B | pass |
| Refuse an install of a GitHub App that was never created | POST /v1/platforms/github/install | 404 | 1.00 | 30 B | pass |
| Refuse an install with no registration named | POST /v1/platforms/github/install | 404 | 1.00 | 30 B | pass |
| Refuse a token the platform cannot check, and never echo it | POST /v1/platforms/tokens | 400 | 1.00 | 92 B | pass |
| Refuse a token for a platform the plaza does not know | POST /v1/platforms/tokens | 400 | 0.00 | 45 B | pass |
| Refuse a token with a level the plaza does not know | POST /v1/platforms/tokens | 400 | 1.00 | 49 B | pass |
| Refuse a repository-level token that names no repository | POST /v1/platforms/tokens | 400 | 1.00 | 71 B | pass |
| Refuse a blank token | POST /v1/platforms/tokens | 400 | 1.00 | 29 B | pass |
| Refuse a token against a host that is not an http(s) URL | POST /v1/platforms/tokens | 400 | 1.00 | 72 B | pass |
| Refuse a token request with no body | POST /v1/platforms/tokens | 400 | 1.00 | 45 B | pass |
| Answer 404 when reading an unknown authorization again | POST /v1/platforms/gitlab-99/sync | 404 | 1.00 | 33 B | pass |
| Answer 404 when forgetting an unknown authorization | DELETE /v1/platforms/gitlab-99 | 404 | 0.00 | 33 B | pass |
| Answer 404 for the namespaces of an unknown authorization | GET /v1/platforms/gitlab-99/namespaces | 404 | 3.00 | 33 B | pass |
| Answer 404 for the repositories of an unknown authorization | GET /v1/platforms/gitlab-99/repos?namespace=t-platforms-group | 404 | 1.00 | 33 B | pass |
| Refuse a repositories list with no namespace on an unknown authorization | GET /v1/platforms/gitlab-99/repos | 404 | 1.00 | 33 B | pass |
| Answer 404 when choosing the grants of an unknown authorization | PUT /v1/platforms/gitlab-99/grants | 404 | 1.00 | 33 B | pass |
| Answer 404 for a grants body of the wrong shape on an unknown authorization | PUT /v1/platforms/gitlab-99/grants | 404 | 1.00 | 33 B | pass |
| Answer 404 when connecting a repository of an unknown authorization | POST /v1/platforms/gitlab-99/connect | 404 | 1.00 | 33 B | pass |
| Answer 404 when disconnecting on an unknown authorization | POST /v1/platforms/gitlab-99/disconnect | 404 | 1.00 | 33 B | pass |
| Answer 404 for a flow that was never started | GET /v1/platforms/flows/0123456789abcdef0123456789abcdef | 404 | 1.00 | 24 B | pass |
| Show the expired page for the go link of a flow that was never started | GET /v1/platforms/flows/0123456789abcdef0123456789abcdef/go | 400 | 0.00 | 487 B | pass |
| Answer 404 when forgetting an app that was never registered | DELETE /v1/platforms/registrations/gitlab-app-999 | 404 | 1.00 | 23 B | pass |
| Refuse a callback whose state was never started, with a platform error | GET /v1/platforms/callback?error=access_denied&state=0123456789abcdef0123456789abcdef | 400 | 1.00 | 484 B | pass |
| List the git platforms on a fresh hub | GET /v1/platforms | 200 | 0.00 | 323 B | pass |
| Start creating the GitHub App | POST /v1/platforms/github/app | 200 | 1.00 | 129 B | pass |
| A callback with an unknown state does nothing | GET /v1/platforms/callback?code=x&state=unknown | 400 | 0.00 | 501 B | pass |
| An organisation-level token must name its organisation | POST /v1/platforms/tokens | 400 | 2.00 | 71 B | pass |
projects-life
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Open a project for the owner with a title and a goal | POST /v1/projects | 201 | 8.00 | 675 B | pass |
| Read the project back in its opening state | GET /v1/projects/pr6 | 200 | 0.00 | 675 B | pass |
| List the projects includes the new one | GET /v1/projects | 200 | 1.00 | 4.5 KiB | pass |
| Read the new project's task graph: no tasks and nothing waiting | GET /v1/projects/pr6/graph | 200 | 1.00 | 110 B | pass |
| Refuse a project with no title | POST /v1/projects | 400 | 1.00 | 45 B | pass |
| Refuse a project whose title is longer than 120 characters | POST /v1/projects | 400 | 0.00 | 45 B | pass |
| Refuse a project with an empty goal | POST /v1/projects | 400 | 1.00 | 45 B | pass |
| Refuse a project kind that is neither code nor other | POST /v1/projects | 400 | 0.00 | 38 B | pass |
| Refuse a project body that is not JSON | POST /v1/projects | 400 | 1.00 | 45 B | pass |
| Open an other-kind project with a unicode title | POST /v1/projects | 201 | 415 | 686 B | pass |
| Refuse approval of a project whose plan is not awaiting approval | POST /v1/projects/pr6/approve | 409 | 3.00 | 64 B | pass |
| Refuse a veto to a stage the project has not reached yet | POST /v1/projects/pr6/veto | 409 | 1.00 | 74 B | pass |
| Refuse a veto to a stage that does not exist | POST /v1/projects/pr6/veto | 409 | 0.00 | 34 B | pass |
| Refuse a mode that does not exist | POST /v1/projects/pr6/mode | 400 | 1.00 | 34 B | pass |
| Refuse a mode change with no mode named | POST /v1/projects/pr6/mode | 400 | 1.00 | 24 B | pass |
| Put the project in build mode it is already in | POST /v1/projects/pr6/mode | 200 | 0.00 | 675 B | pass |
| Adopt a finished project: it opens delivered, in maintenance | POST /v1/projects/adopt | 201 | 8.00 | 1010 B | pass |
| Refuse an adoption with no title | POST /v1/projects/adopt | 400 | 0.00 | 63 B | pass |
| Refuse an adoption with a kind that is neither code nor other | POST /v1/projects/adopt | 400 | 1.00 | 38 B | pass |
| Read the project's settings overrides: none yet | GET /v1/projects/pr6/settings | 200 | 1.00 | 2 B | pass |
| Set the project's default task budget in its settings | PATCH /v1/projects/pr6/settings | 200 | 1.00 | 19 B | pass |
| Read the effective settings: the task budget comes from the project | GET /v1/projects/pr6/settings/effective | 200 | 2.00 | 745 B | pass |
| Refuse a settings change that names no such setting | PATCH /v1/projects/pr6/settings | 400 | 1.00 | 510 B | pass |
| Refuse a permission mode that does not exist | PATCH /v1/projects/pr6/settings | 400 | 1.00 | 84 B | pass |
| Create a task with no budget: it takes the project's task budget | POST /v1/tasks | 201 | 1.00 | 300 B | pass |
| Clear the project's task budget setting: the plaza's default applies again | PATCH /v1/projects/pr6/settings | 200 | 1.00 | 2 B | pass |
| Put the project in build mode: a no-op for the active project | POST /v1/projects/pr6/mode | 200 | 1.00 | 762 B | pass |
| Pause the project: its agents' work waits | POST /v1/projects/pr6/pause | 200 | 1.00 | 835 B | pass |
| Pausing a paused project changes nothing | POST /v1/projects/pr6/pause | 200 | 1.00 | 835 B | pass |
| Read the paused project: still in its stage, its history records the pause | GET /v1/projects/pr6 | 200 | 1.00 | 835 B | pass |
| A task added to a paused project waits: it is not refused | POST /v1/tasks | 201 | 1.00 | 298 B | pass |
| Refuse a maintenance sweep of a paused project | POST /v1/projects/pr6/maintenance/sweep | 409 | 1.00 | 45 B | pass |
| Resume the paused project: its waiting work goes out | POST /v1/projects/pr6/resume | 200 | 2.00 | 999 B | pass |
| Resuming a project that is not paused changes nothing | POST /v1/projects/pr6/resume | 200 | 1.00 | 999 B | pass |
| The task added while paused is still open after the resume | GET /v1/tasks/t6 | 200 | 1.00 | 298 B | pass |
| The project's graph lists the resumed work as ready | GET /v1/projects/pr6/graph | 200 | 1.00 | 479 B | pass |
| Put the project in maintenance mode | POST /v1/projects/pr6/mode | 200 | 1.00 | 1.1 KiB | pass |
| Read the maintenance view: the default interval, no signals, no proposals | GET /v1/projects/pr6/maintenance | 200 | 1.00 | 357 B | pass |
| Change the maintenance settings: every 6 hours, with a focus | PATCH /v1/projects/pr6/maintenance | 200 | 1.00 | 365 B | pass |
| Refuse a maintenance interval over 720 hours | PATCH /v1/projects/pr6/maintenance | 400 | 1.00 | 40 B | pass |
| Refuse a watcher that is not on the project | PATCH /v1/projects/pr6/maintenance | 400 | 1.00 | 44 B | pass |
| Refuse a maintenance interval that is not a number | PATCH /v1/projects/pr6/maintenance | 400 | 1.00 | 40 B | pass |
| Refuse a maintenance sweep with no agents to watch the project | POST /v1/projects/pr6/maintenance/sweep | 409 | 1.00 | 101 B | pass |
| Add a signal to the project's inbox | POST /v1/projects/pr6/maintenance/signals | 201 | 2.00 | 120 B | pass |
| Refuse a signal with no text | POST /v1/projects/pr6/maintenance/signals | 400 | 1.00 | 25 B | pass |
| Delete the signal from the inbox | DELETE /v1/projects/pr6/maintenance/signals/s1 | 200 | 1.00 | 16 B | pass |
| Refuse to delete a signal that does not exist | DELETE /v1/projects/pr6/maintenance/signals/s999999 | 404 | 1.00 | 26 B | pass |
| Make the project's ingest address | POST /v1/projects/pr6/maintenance/ingest | 201 | 1.00 | 139 B | pass |
| Making a new ingest address stops the old one | POST /v1/projects/pr6/maintenance/ingest | 201 | 1.00 | 139 B | pass |
| The old ingest address no longer takes signals | POST /v1/ingest/apzi_31060ca20b1aa8bc9332837f9d78a39e0b95e89a1c23fed5 | 404 | 0.00 | 34 B | pass |
| Stop the project's ingest address | DELETE /v1/projects/pr6/maintenance/ingest | 200 | 1.00 | 16 B | pass |
| Stopping an ingest address that is already stopped changes nothing | DELETE /v1/projects/pr6/maintenance/ingest | 200 | 0.00 | 16 B | pass |
| Refuse to decline a proposal that does not exist | POST /v1/projects/pr6/maintenance/proposals/p999999/decline | 404 | 0.00 | 28 B | pass |
| Refuse to put off a proposal that does not exist | POST /v1/projects/pr6/maintenance/proposals/p999999/later | 404 | 1.00 | 28 B | pass |
| Put a development environment on the project | PUT /v1/projects/pr6/environments/probe-env | 200 | 2.00 | 211 B | pass |
| List the environments: the new one is there | GET /v1/projects/pr6/environments | 200 | 1.00 | 213 B | pass |
| Refuse an environment name with capital letters | PUT /v1/projects/pr6/environments/Probe_Env | 400 | 1.00 | 98 B | pass |
| Refuse an environment kind that does not exist | PUT /v1/projects/pr6/environments/probe-env | 400 | 1.00 | 80 B | pass |
| Set a secret for the development environment | PUT /v1/projects/pr6/environments/probe-env/secrets/PROBE_TOKEN | 200 | 30.0 | 56 B | pass |
| Remove the environment's secret | DELETE /v1/projects/pr6/environments/probe-env/secrets/PROBE_TOKEN | 200 | 18.0 | 16 B | pass |
| Refuse to remove a secret that the environment does not have | DELETE /v1/projects/pr6/environments/probe-env/secrets/NOT_THERE | 404 | 0.00 | 26 B | pass |
| Remove the environment and its secrets | DELETE /v1/projects/pr6/environments/probe-env | 200 | 1.00 | 16 B | pass |
| Refuse to remove an environment that is already gone | DELETE /v1/projects/pr6/environments/probe-env | 404 | 1.00 | 31 B | pass |
| Refuse a maintenance settings body that is a JSON string, not an object | PATCH /v1/projects/pr6/maintenance | 400 | 1.00 | 42 B | pass |
| Archive the project: its open tasks are withdrawn and its budget goes back | POST /v1/projects/pr6/archive | 200 | 1.00 | 1.1 KiB | pass |
| Archiving an archived project changes nothing | POST /v1/projects/pr6/archive | 200 | 0.00 | 1.1 KiB | pass |
| The default-budget task is withdrawn | GET /v1/tasks/t5 | 200 | 3.00 | 313 B | pass |
| The paused-period task is withdrawn too | GET /v1/tasks/t6 | 200 | 0.00 | 311 B | pass |
| Refuse to pause an archived project | POST /v1/projects/pr6/pause | 409 | 12.0 | 35 B | pass |
| Refuse to resume an archived project | POST /v1/projects/pr6/resume | 409 | 1.00 | 35 B | pass |
| Refuse a mode change on an archived project | POST /v1/projects/pr6/mode | 409 | 1.00 | 35 B | pass |
| Refuse a new task on an archived project | POST /v1/tasks | 409 | 1.00 | 58 B | pass |
| Refuse a maintenance sweep of an archived project | POST /v1/projects/pr6/maintenance/sweep | 409 | 0.00 | 45 B | pass |
| Read the archived project: its history records the archive | GET /v1/projects/pr6 | 200 | 1.00 | 1.1 KiB | pass |
| Read the archived project's graph: none of its tasks is still open | GET /v1/projects/pr6/graph | 200 | 1.00 | 445 B | pass |
| Open a project for the veto branch | POST /v1/projects | 201 | 25.0 | 663 B | pass |
| Veto the project back to brief: its plan is no longer approved | POST /v1/projects/pr9/veto | 200 | 1.00 | 766 B | pass |
| Refuse a veto to a later stage than the project is at | POST /v1/projects/pr9/veto | 409 | 1.00 | 65 B | pass |
| Refuse a veto with no stage: it defaults to proposal, later than brief | POST /v1/projects/pr9/veto | 409 | 1.00 | 65 B | pass |
| Refuse a task while the plan is at brief | POST /v1/tasks | 409 | 2.00 | 87 B | pass |
| Refuse approval while the plan is at brief | POST /v1/projects/pr9/approve | 409 | 1.00 | 55 B | pass |
| Read the vetoed project: still at brief | GET /v1/projects/pr9 | 200 | 1.00 | 766 B | pass |
| Open a project for the paused-then-archived branch | POST /v1/projects | 201 | 8.00 | 700 B | pass |
| Create a task with a budget of 200 | POST /v1/tasks | 201 | 1.00 | 301 B | pass |
| Pause the project | POST /v1/projects/pr10/pause | 200 | 1.00 | 860 B | pass |
| Archive the paused project: the pause ends with it | POST /v1/projects/pr10/archive | 200 | 1.00 | 936 B | pass |
| The task is withdrawn | GET /v1/tasks/t7 | 200 | 1.00 | 314 B | pass |
| Refuse to resume the archived project | POST /v1/projects/pr10/resume | 409 | 1.00 | 36 B | pass |
| Answer 404 for a project that does not exist | GET /v1/projects/pr999999 | 404 | 0.00 | 27 B | pass |
| Answer 404 when pausing a project that does not exist | POST /v1/projects/pr999999/pause | 404 | 1.00 | 27 B | pass |
| Answer 404 when resuming a project that does not exist | POST /v1/projects/pr999999/resume | 404 | 2.00 | 27 B | pass |
| Answer 404 when archiving a project that does not exist | POST /v1/projects/pr999999/archive | 404 | 0.00 | 27 B | pass |
| Answer 404 when changing the mode of a project that does not exist | POST /v1/projects/pr999999/mode | 404 | 1.00 | 27 B | pass |
| Answer 404 for the graph of a project that does not exist | GET /v1/projects/pr999999/graph | 404 | 1.00 | 27 B | pass |
| Answer 404 for the budget of a project that does not exist | GET /v1/projects/pr999999/budget | 404 | 0.00 | 31 B | pass |
| Answer 404 for the maintenance of a project that does not exist | GET /v1/projects/pr999999/maintenance | 404 | 0.00 | 27 B | pass |
| Answer 404 for the settings of a project that does not exist | GET /v1/projects/pr999999/settings | 404 | 1.00 | 27 B | pass |
| Answer 404 for the environments of a project that does not exist | GET /v1/projects/pr999999/environments | 404 | 0.00 | 27 B | pass |
| Answer 404 for a project id that is not a project at all | GET /v1/projects/not-a-project | 404 | 0.00 | 27 B | pass |
| A project's build plan: targets on the pools that build them | GET /v1/projects/pr2/build-plan | 200 | 2.00 | 14 B | pass |
| List the project modes | GET /v1/project-modes | 200 | 1.00 | 371 B | pass |
| Bring in a finished project from a git address | POST /v1/projects/adopt | 201 | 23.0 | 1.4 KiB | pass |
| Put the project back in build | POST /v1/projects/pr11/mode | 200 | 0.00 | 1007 B | pass |
| Answer 400 for a mode that does not exist | POST /v1/projects/pr11/mode | 400 | 2.00 | 35 B | pass |
| Get the maintenance view of a fresh project | GET /v1/projects/pr11/maintenance | 200 | 0.00 | 358 B | pass |
| Add a signal to the project's inbox | POST /v1/projects/pr11/maintenance/signals | 201 | 1.00 | 183 B | pass |
| Make the project's ingest address | POST /v1/projects/pr11/maintenance/ingest | 201 | 1.00 | 139 B | pass |
| Send a signal to the ingest address without a token | POST /v1/ingest/apzi_6032e8b3ff0d4216b309dc78884fbb75c27b3ca9bb0a9796 | 202 | 1.00 | 11 B | pass |
| Answer 404 for an ingest address that does not exist | POST /v1/ingest/apzi_not-a-real-address | 404 | 1.00 | 34 B | pass |
| Answer 404 when approving a proposal that does not exist | POST /v1/projects/pr11/maintenance/proposals/mp999/approve | 404 | 0.00 | 28 B | pass |
| List the project's environments | GET /v1/projects/pr11/environments | 200 | 1.00 | 247 B | pass |
| Put a staging environment that tracks main | PUT /v1/projects/pr11/environments/staging | 200 | 1.00 | 239 B | pass |
| Deploy a named version to staging | POST /v1/projects/pr11/environments/staging/deploy | 200 | 1.00 | 96 B | pass |
| Put a secret for the staging environment | PUT /v1/projects/pr11/environments/staging/secrets/STAGING_DEPLOY_TOKEN | 200 | 65.0 | 64 B | pass |
| Upload a file's bytes into the project | PUT /v1/projects/pr11/uploads | 201 | 44.0 | 85 B | pass |
| Land the uploaded file on main as one change | POST /v1/projects/pr11/files | 200 | 47.0 | 110 B | pass |
| A project's test topologies: typed, one per file under .plaza/topologies | GET /v1/projects/pr11/topologies | 200 | 1.00 | 17 B | pass |
| Topologies of an unknown project | GET /v1/projects/no-such-project/topologies | 404 | 0.00 | 27 B | pass |
| Importing a topology into an unknown project | POST /v1/projects/no-such-project/topologies/import | 404 | 1.00 | 27 B | pass |
| A topology import from a source it does not read | POST /v1/projects/pr11/topologies/import | 400 | 0.00 | 69 B | pass |
| A Compose file that is not on main is not imported | POST /v1/projects/pr11/topologies/import | 404 | 2.00 | 45 B | pass |
looks
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Refuse an agent's artifact without the internal token | PUT /internal/v1/agents/5/gateway/artifacts | 403 | 1.00 | 43 B | pass |
| Refuse an agent's artifact with a wrong internal token | PUT /internal/v1/agents/5/gateway/artifacts | 403 | 1.00 | 43 B | pass |
| Refuse an agent's artifact from a cid that is not a number | PUT /internal/v1/agents/abc/gateway/artifacts | 400 | 1.00 | 36 B | pass |
| Refuse an artifact with no panels | PUT /internal/v1/agents/5/gateway/artifacts | 400 | 18.0 | 105 B | pass |
| Refuse a panel whose span is not a width of the grid | PUT /internal/v1/agents/5/gateway/artifacts | 400 | 10.0 | 99 B | pass |
| Refuse an artifact body that is not JSON | PUT /internal/v1/agents/5/gateway/artifacts | 400 | 1.00 | 101 B | pass |
| Make the artifact t-looks Notes as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 39.0 | 105 B | pass |
| Read the artifact as the owner | GET /v1/artifacts/ac7yadxhfch2 | 200 | 2.00 | 343 B | pass |
| Read the artifact as the agent that made it | GET /internal/v1/agents/5/gateway/artifacts/ac7yadxhfch2 | 200 | 1.00 | 343 B | pass |
| Refuse an agent reading an artifact another agent made | GET /internal/v1/agents/6/gateway/artifacts/ac7yadxhfch2 | 404 | 1.00 | 28 B | pass |
| List the artifacts with a query that matches the title | GET /v1/artifacts?q=t-looks%20Notes | 200 | 1.00 | 164 B | pass |
| List the artifacts of a project | GET /v1/artifacts?project=t-looks | 200 | 1.00 | 164 B | pass |
| List the artifacts of a project that has none | GET /v1/artifacts?project=t-nope-project | 200 | 1.00 | 2 B | pass |
| Refuse a version that is not a number | GET /v1/artifacts/ac7yadxhfch2?v=abc | 400 | 1.00 | 46 B | pass |
| Refuse a version that does not exist | GET /v1/artifacts/ac7yadxhfch2?v=99 | 404 | 1.00 | 39 B | pass |
| Refuse the versions of an artifact that does not exist | GET /v1/artifacts/aunknownid00/versions | 404 | 1.00 | 28 B | pass |
| Add a second version to the artifact as its author | PUT /internal/v1/agents/5/gateway/artifacts/ac7yadxhfch2 | 201 | 24.0 | 107 B | pass |
| Refuse a new version from an agent that did not make the artifact | PUT /internal/v1/agents/6/gateway/artifacts/ac7yadxhfch2 | 403 | 1.00 | 56 B | pass |
| Refuse a new version of an artifact that does not exist | PUT /internal/v1/agents/5/gateway/artifacts/aunknownid00 | 404 | 1.00 | 28 B | pass |
| Refuse a new version with no spec | PUT /internal/v1/agents/5/gateway/artifacts/ac7yadxhfch2 | 400 | 1.00 | 51 B | pass |
| Refuse a new version whose panels are empty | PUT /internal/v1/agents/5/gateway/artifacts/ac7yadxhfch2 | 400 | 12.0 | 105 B | pass |
| Read the first version after a second one exists | GET /v1/artifacts/ac7yadxhfch2?v=1 | 200 | 1.00 | 343 B | pass |
| Read the newest version when no version is named | GET /v1/artifacts/ac7yadxhfch2 | 200 | 1.00 | 334 B | pass |
| List the versions oldest first | GET /v1/artifacts/ac7yadxhfch2/versions | 200 | 1.00 | 131 B | pass |
| Register agent 5 through its VM hello, so the agent routes take its uploads | POST /internal/v1/agents/5/hello | 200 | 8.00 | 55.0 KiB | pass |
| Store a CSV data blob for the artifact | PUT /internal/v1/agents/5/gateway/artifacts/blobs?content_type=text/csv | 201 | 60.0 | 72 B | pass |
| Refuse a data blob with an empty body | PUT /internal/v1/agents/5/gateway/artifacts/blobs?content_type=text/csv | 400 | 1.00 | 29 B | pass |
| Add a version that reads the CSV blob in a dataset panel | PUT /internal/v1/agents/5/gateway/artifacts/ac7yadxhfch2 | 201 | 33.0 | 108 B | pass |
| Read one page of the dataset's rows | GET /v1/artifacts/ac7yadxhfch2/rows?panel=sales&limit=1 | 200 | 1.00 | 154 B | pass |
| Sort and filter the dataset's rows | GET /v1/artifacts/ac7yadxhfch2/rows?panel=sales&sort=total&desc=1&filter=north | 200 | 1.00 | 154 B | pass |
| Refuse rows of a panel that is not a dataset | GET /v1/artifacts/ac7yadxhfch2/rows?panel=notes | 400 | 1.00 | 44 B | pass |
| Refuse rows of a panel that does not exist | GET /v1/artifacts/ac7yadxhfch2/rows?panel=nope | 400 | 1.00 | 29 B | pass |
| Refuse rows of an artifact that does not exist | GET /v1/artifacts/aunknownid00/rows?panel=sales | 404 | 1.00 | 28 B | pass |
| Read the CSV blob the dataset references | GET /v1/artifacts/ac7yadxhfch2/blobs/b1d8ea613033ff4fd | 200 | 1.00 | 30 B | pass |
| Refuse a blob the artifact does not reference | GET /v1/artifacts/ac7yadxhfch2/blobs/b0000000000000000 | 404 | 1.00 | 24 B | pass |
| Refuse a blob of an artifact that does not exist | GET /v1/artifacts/aunknownid00/blobs/b0000000000000000 | 404 | 1.00 | 24 B | pass |
| Export the dataset panel as CSV | GET /v1/artifacts/ac7yadxhfch2/export?format=csv&panel=sales | 200 | 1.00 | 37 B | pass |
| Export the whole artifact as JSON by default | GET /v1/artifacts/ac7yadxhfch2/export | 200 | 1.00 | 971 B | pass |
| Export the chart panel as PNG | GET /v1/artifacts/ac7yadxhfch2/export?format=png&panel=trend | 200 | 11.0 | 69.5 KiB | pass |
| Refuse a CSV export without a panel | GET /v1/artifacts/ac7yadxhfch2/export?format=csv | 400 | 2.00 | 50 B | pass |
| Refuse a PNG export of a table panel | GET /v1/artifacts/ac7yadxhfch2/export?format=png&panel=sales | 400 | 1.00 | 68 B | pass |
| Refuse an export in a format that does not exist | GET /v1/artifacts/ac7yadxhfch2/export?format=xml | 400 | 1.00 | 51 B | pass |
| Refuse a CSV export of a panel that does not exist | GET /v1/artifacts/ac7yadxhfch2/export?format=csv&panel=nope | 400 | 1.00 | 29 B | pass |
| Refuse an export of a version that does not exist | GET /v1/artifacts/ac7yadxhfch2/export?v=99 | 404 | 1.00 | 39 B | pass |
| Delete agent 5 again, so later folders do not see it | DELETE /v1/agents/5 | 200 | 1.00 | 34 B | pass |
| Delete the artifact and the data only it uses | DELETE /v1/artifacts/ac7yadxhfch2 | 200 | 119 | 26 B | pass |
| Read the deleted artifact | GET /v1/artifacts/ac7yadxhfch2 | 404 | 0.00 | 39 B | pass |
| Delete the artifact a second time | DELETE /v1/artifacts/ac7yadxhfch2 | 404 | 38.0 | 28 B | pass |
| Refuse rows of the deleted artifact | GET /v1/artifacts/ac7yadxhfch2/rows?panel=sales | 404 | 1.00 | 28 B | pass |
| The deleted artifact's data blob is gone | GET /v1/artifacts/ac7yadxhfch2/blobs/b1d8ea613033ff4fd | 404 | 0.00 | 24 B | pass |
| The agent cannot read its deleted artifact | GET /internal/v1/agents/5/gateway/artifacts/ac7yadxhfch2 | 404 | 1.00 | 28 B | pass |
| The deleted artifact is not listed | GET /v1/artifacts?q=t-looks%20Notes | 200 | 0.00 | 2 B | pass |
| Make the artifact t-looks share 01 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 79.0 | 108 B | pass |
| Create a live share link of the artifact | POST /v1/shares | 201 | 37.0 | 461 B | pass |
| List the artifacts shows the artifact as shared | GET /v1/artifacts?q=t-looks%20share%2001 | 200 | 0.00 | 146 B | pass |
| Read the shared artifact through its link | GET /artifact/e3147a278fd73c6082d8c9bc747b28c3/data | 200 | 33.0 | 555 B | pass |
| The share counts the view | GET /v1/shares?target=artifact:anraelfeztvf | 200 | 1.00 | 463 B | pass |
| The view is in the share's events, through the network link | GET /v1/shares/e3147a278fd73c6082d8c9bc747b28c3/events | 200 | 1.00 | 98 B | pass |
| Refuse the events of an artifact id, which is not a share | GET /v1/shares/anraelfeztvf/events | 404 | 0.00 | 25 B | pass |
| Make the artifact t-looks share 02 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 48.0 | 108 B | pass |
| Create a share with a password | POST /v1/shares | 201 | 58.0 | 460 B | pass |
| Refuse a forged unlock cookie | GET /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/data | 401 | 0.00 | 23 B | pass |
| Read a password-protected share without unlocking it | GET /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/data | 401 | 2.00 | 23 B | pass |
| Refuse a wrong password | POST /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/unlock | 403 | 55.0 | 26 B | pass |
| Refuse an unlock without a password field | POST /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/unlock | 400 | 1.00 | 56 B | pass |
| Unlock with the right password and keep the unlock cookie | POST /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/unlock | 204 | 44.0 | 0 B | pass |
| Read the share with the unlock cookie | GET /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/data | 200 | 29.0 | 555 B | pass |
| Change the password | PATCH /v1/shares/c6fa69f2a9007e14732fb2cbc7a5c27c | 200 | 39.0 | 460 B | pass |
| The old unlock cookie stops working after the password changes | GET /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/data | 401 | 1.00 | 23 B | pass |
| Unlock with the new password | POST /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/unlock | 204 | 24.0 | 0 B | pass |
| Clear the password | PATCH /v1/shares/c6fa69f2a9007e14732fb2cbc7a5c27c | 200 | 12.0 | 461 B | pass |
| Read the share without a password or cookie | GET /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/data | 200 | 26.0 | 555 B | pass |
| Unlocking a share with no password does nothing and succeeds | POST /artifact/c6fa69f2a9007e14732fb2cbc7a5c27c/unlock | 204 | 1.00 | 0 B | pass |
| Make the artifact t-looks share 03 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 63.0 | 108 B | pass |
| Create a share with a password for the lockout | POST /v1/shares | 201 | 66.0 | 460 B | pass |
| Unlock with a wrong password, attempt 1 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 33.0 | 26 B | pass |
| Unlock with a wrong password, attempt 2 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 27.0 | 26 B | pass |
| Unlock with a wrong password, attempt 3 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 33.0 | 26 B | pass |
| Unlock with a wrong password, attempt 4 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 33.0 | 26 B | pass |
| Unlock with a wrong password, attempt 5 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 27.0 | 26 B | pass |
| Unlock with a wrong password, attempt 6 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 27.0 | 26 B | pass |
| Unlock with a wrong password, attempt 7 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 46.0 | 26 B | pass |
| Unlock with a wrong password, attempt 8 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 27.0 | 26 B | pass |
| Unlock with a wrong password, attempt 9 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 42.0 | 26 B | pass |
| Unlock with a wrong password, attempt 10 of ten | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 403 | 72.0 | 26 B | pass |
| Refuse the right password after ten failed unlocks | POST /artifact/13faeed558ad7fe8f9cb4e10c0957525/unlock | 429 | 1.00 | 46 B | pass |
| Reading the locked share still needs the password | GET /artifact/13faeed558ad7fe8f9cb4e10c0957525/data | 401 | 1.00 | 23 B | pass |
| Make the artifact t-looks share 04 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 41.0 | 108 B | pass |
| Create a share pinned to version 1 | POST /v1/shares | 201 | 30.0 | 458 B | pass |
| Add version 2 to the artifact | PUT /internal/v1/agents/5/gateway/artifacts/a2k53nyckmy5 | 201 | 48.0 | 110 B | pass |
| The pinned link keeps serving version 1 | GET /artifact/d98ea1e3e967405655bde98aa78b4e30/data | 200 | 11.0 | 542 B | pass |
| Clear the pinned version | PATCH /v1/shares/d98ea1e3e967405655bde98aa78b4e30 | 200 | 22.0 | 461 B | pass |
| With the pin cleared the link serves the latest version | GET /artifact/d98ea1e3e967405655bde98aa78b4e30/data | 200 | 22.0 | 545 B | pass |
| Pin the version back to 1 | PATCH /v1/shares/d98ea1e3e967405655bde98aa78b4e30 | 200 | 14.0 | 458 B | pass |
| The link serves version 1 again | GET /artifact/d98ea1e3e967405655bde98aa78b4e30/data | 200 | 8.00 | 542 B | pass |
| Make the artifact t-looks share 05 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 44.0 | 109 B | pass |
| Create a share with downloads off by default | POST /v1/shares | 201 | 31.0 | 461 B | pass |
| Refuse a JSON export while downloads are off | GET /artifact/dc70a7d36a3300729bb9153dd1970d8c/export?format=json | 403 | 1.00 | 44 B | pass |
| Refuse a CSV export while downloads are off | GET /artifact/dc70a7d36a3300729bb9153dd1970d8c/export?format=csv&panel=sales | 403 | 1.00 | 44 B | pass |
| Turn downloads on | PATCH /v1/shares/dc70a7d36a3300729bb9153dd1970d8c | 200 | 7.00 | 460 B | pass |
| Export the artifact as JSON with downloads on | GET /artifact/dc70a7d36a3300729bb9153dd1970d8c/export?format=json | 200 | 8.00 | 770 B | pass |
| Export the dataset panel as CSV | GET /artifact/dc70a7d36a3300729bb9153dd1970d8c/export?format=csv&panel=sales | 200 | 11.0 | 33 B | pass |
| Export the chart panel as PNG | GET /artifact/dc70a7d36a3300729bb9153dd1970d8c/export?format=png&panel=trend | 200 | 35.0 | 69.5 KiB | pass |
| Refuse a CSV export without a panel | GET /artifact/dc70a7d36a3300729bb9153dd1970d8c/export?format=csv | 400 | 1.00 | 50 B | pass |
| Refuse an export format that does not exist | GET /artifact/dc70a7d36a3300729bb9153dd1970d8c/export?format=xml | 400 | 1.00 | 51 B | pass |
| Refuse a PNG of a table panel | GET /artifact/dc70a7d36a3300729bb9153dd1970d8c/export?format=png&panel=sales | 400 | 2.00 | 68 B | pass |
| The events record the downloads | GET /v1/shares/dc70a7d36a3300729bb9153dd1970d8c/events | 200 | 1.00 | 304 B | pass |
| Turn downloads off again | PATCH /v1/shares/dc70a7d36a3300729bb9153dd1970d8c | 200 | 19.0 | 461 B | pass |
| Refuse a JSON export after downloads are turned off | GET /artifact/dc70a7d36a3300729bb9153dd1970d8c/export?format=json | 403 | 1.00 | 44 B | pass |
| Make the artifact t-looks share 06 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 93.0 | 108 B | pass |
| Create the first share link | POST /v1/shares | 201 | 120 | 461 B | pass |
| Create the second share link | POST /v1/shares | 201 | 15.0 | 461 B | pass |
| Read the first link before revoking it | GET /artifact/7315751b8a6ee53dbc54ab1fc6974ab7/data | 200 | 8.00 | 555 B | pass |
| Revoke the first link | DELETE /v1/shares/7315751b8a6ee53dbc54ab1fc6974ab7 | 200 | 7.00 | 473 B | pass |
| A revoked link answers as a missing one | GET /artifact/7315751b8a6ee53dbc54ab1fc6974ab7/data | 404 | 1.00 | 19 B | pass |
| A revoked link cannot be unlocked | POST /artifact/7315751b8a6ee53dbc54ab1fc6974ab7/unlock | 404 | 0.00 | 19 B | pass |
| A revoked link cannot be exported | GET /artifact/7315751b8a6ee53dbc54ab1fc6974ab7/export?format=json | 404 | 1.00 | 19 B | pass |
| Revoke the first link a second time | DELETE /v1/shares/7315751b8a6ee53dbc54ab1fc6974ab7 | 200 | 20.0 | 473 B | pass |
| The artifact stays shared while the second link is live | GET /v1/artifacts?q=t-looks%20share%2006 | 200 | 2.00 | 146 B | pass |
| Revoke the second link | DELETE /v1/shares/c4cd16d69ca1d51d8da1838b93cb6f41 | 200 | 14.0 | 473 B | pass |
| The artifact is not shared once no link is live | GET /v1/artifacts?q=t-looks%20share%2006 | 200 | 0.00 | 147 B | pass |
| The events of a revoked link stay readable | GET /v1/shares/7315751b8a6ee53dbc54ab1fc6974ab7/events | 200 | 1.00 | 98 B | pass |
| Revoke a share that does not exist | DELETE /v1/shares/00000000000000000000000000000000 | 404 | 8.00 | 25 B | pass |
| Make the artifact t-looks share 07 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 22.0 | 108 B | pass |
| Create a share that expired at once | POST /v1/shares | 201 | 17.0 | 461 B | pass |
| Read an expired link | GET /artifact/31c0569f37a7482ff129bdc4a9bca68a/data | 410 | 0.00 | 19 B | pass |
| Unlocking an expired link is refused as expired | POST /artifact/31c0569f37a7482ff129bdc4a9bca68a/unlock | 410 | 0.00 | 19 B | pass |
| Create a link that lasts one day | POST /v1/shares | 201 | 23.0 | 470 B | pass |
| Read a link that is still live | GET /artifact/5def2fbcacf30c875cf89ad84d6b353e/data | 200 | 8.00 | 555 B | pass |
| Move the expiry into the past | PATCH /v1/shares/5def2fbcacf30c875cf89ad84d6b353e | 200 | 9.00 | 461 B | pass |
| Read the link after its expiry was moved into the past | GET /artifact/5def2fbcacf30c875cf89ad84d6b353e/data | 410 | 1.00 | 19 B | pass |
| Clear the expiry | PATCH /v1/shares/5def2fbcacf30c875cf89ad84d6b353e | 200 | 12.0 | 461 B | pass |
| Read the link once its expiry is cleared | GET /artifact/5def2fbcacf30c875cf89ad84d6b353e/data | 200 | 8.00 | 555 B | pass |
| Make the artifact t-looks share 08 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 22.0 | 108 B | pass |
| Create a link to the artifact | POST /v1/shares | 201 | 16.0 | 461 B | pass |
| Read the link before the artifact is deleted | GET /artifact/8c5519057c5795d87eeb84d1ddf684f2/data | 200 | 7.00 | 555 B | pass |
| Delete the artifact the link points at | DELETE /v1/artifacts/aisiutkmqsdv | 200 | 9.00 | 26 B | pass |
| The link answers missing once the artifact is gone | GET /artifact/8c5519057c5795d87eeb84d1ddf684f2/data | 404 | 1.00 | 19 B | pass |
| The link's blob is missing too | GET /artifact/8c5519057c5795d87eeb84d1ddf684f2/blobs/b0000000000000000 | 404 | 0.00 | 19 B | pass |
| Revoke the link to the deleted artifact | DELETE /v1/shares/8c5519057c5795d87eeb84d1ddf684f2 | 200 | 22.0 | 473 B | pass |
| Make the artifact t-looks share 09 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 21.0 | 108 B | pass |
| Refuse a share of a file that does not exist | POST /v1/shares | 404 | 0.00 | 36 B | pass |
| Refuse a share of an unknown target kind | POST /v1/shares | 400 | 1.00 | 58 B | pass |
| Refuse a share with an empty id | POST /v1/shares | 400 | 1.00 | 58 B | pass |
| Refuse a share with no target | POST /v1/shares | 400 | 0.00 | 53 B | pass |
| Refuse a share body that is not JSON | POST /v1/shares | 400 | 1.00 | 53 B | pass |
| Refuse a password of seven characters | POST /v1/shares | 400 | 1.00 | 47 B | pass |
| Refuse a negative number of days | POST /v1/shares | 400 | 0.00 | 53 B | pass |
| Refuse an expiry that is not a number | POST /v1/shares | 400 | 1.00 | 53 B | pass |
| An empty password means no password | POST /v1/shares | 201 | 116 | 461 B | pass |
| Revoke the link with the empty password | DELETE /v1/shares/b547939cdac1792813f99ee557790505 | 200 | 25.0 | 473 B | pass |
| Make the artifact t-looks share 10 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 21.0 | 108 B | pass |
| Create a link to change | POST /v1/shares | 201 | 14.0 | 461 B | pass |
| Refuse a change to a link that does not exist | PATCH /v1/shares/00000000000000000000000000000000 | 404 | 8.00 | 25 B | pass |
| Refuse a change whose body is a list | PATCH /v1/shares/7196cbe2580c89d04fcc7f2e2e3562d3 | 400 | 1.00 | 37 B | pass |
| Refuse a password of five characters | PATCH /v1/shares/7196cbe2580c89d04fcc7f2e2e3562d3 | 400 | 0.00 | 47 B | pass |
| Refuse a download setting that is not true or false | PATCH /v1/shares/7196cbe2580c89d04fcc7f2e2e3562d3 | 400 | 0.00 | 43 B | pass |
| Refuse an expiry that is not a number | PATCH /v1/shares/7196cbe2580c89d04fcc7f2e2e3562d3 | 400 | 1.00 | 65 B | pass |
| Refuse a version that is not a number | PATCH /v1/shares/7196cbe2580c89d04fcc7f2e2e3562d3 | 400 | 0.00 | 62 B | pass |
| Refuse a change body that is not JSON | PATCH /v1/shares/7196cbe2580c89d04fcc7f2e2e3562d3 | 400 | 1.00 | 37 B | pass |
| An empty change leaves the link as it was | PATCH /v1/shares/7196cbe2580c89d04fcc7f2e2e3562d3 | 200 | 17.0 | 461 B | pass |
| Revoke the link | DELETE /v1/shares/7196cbe2580c89d04fcc7f2e2e3562d3 | 200 | 25.0 | 473 B | pass |
| Make the artifact t-looks share 11a as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 20.0 | 109 B | pass |
| Make the artifact t-looks share 11b as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 36.0 | 109 B | pass |
| Create a first link to artifact a | POST /v1/shares | 201 | 26.0 | 462 B | pass |
| Create a second link to artifact a | POST /v1/shares | 201 | 16.0 | 462 B | pass |
| Create a link to artifact b | POST /v1/shares | 201 | 20.0 | 462 B | pass |
| List the links of artifact a | GET /v1/shares?target=artifact:axyn6cktpzhv | 200 | 1.00 | 927 B | pass |
| List the links of artifact b | GET /v1/shares?target=artifact:ahdbdrh7w54z | 200 | 1.00 | 464 B | pass |
| Read the link to artifact a once, to make an event | GET /artifact/25187658c916c798f643349dfb2e3f95/data | 200 | 56.0 | 557 B | pass |
| Limit the events to one | GET /v1/shares/25187658c916c798f643349dfb2e3f95/events?limit=1 | 200 | 1.00 | 98 B | pass |
| A limit of zero is read as one | GET /v1/shares/25187658c916c798f643349dfb2e3f95/events?limit=0 | 200 | 1.00 | 98 B | pass |
| A limit above the most events is capped | GET /v1/shares/25187658c916c798f643349dfb2e3f95/events?limit=5000 | 200 | 2.00 | 98 B | pass |
| Refuse a target filter that is not a target | GET /v1/shares?target=bogus | 400 | 1.00 | 48 B | pass |
| Refuse a target filter of an unknown kind | GET /v1/shares?target=other:x | 400 | 0.00 | 48 B | pass |
| A file target with no shares lists nothing | GET /v1/shares?target=file:fnope | 200 | 1.00 | 2 B | pass |
| Revoke the first link to artifact a | DELETE /v1/shares/25187658c916c798f643349dfb2e3f95 | 200 | 24.0 | 474 B | pass |
| Revoke the second link to artifact a | DELETE /v1/shares/6918ffccaee02a3a71fe912aa6f91e47 | 200 | 50.0 | 474 B | pass |
| Revoke the link to artifact b | DELETE /v1/shares/9908e0bb906eae4a511a6dca45b473d9 | 200 | 42.0 | 474 B | pass |
| Register agent 5 through its VM hello, so the agent routes take its uploads | POST /internal/v1/agents/5/hello | 200 | 3.00 | 55.0 KiB | pass |
| Store a picture blob for the artifact | PUT /internal/v1/agents/5/gateway/artifacts/blobs?content_type=image/png | 201 | 86.0 | 73 B | pass |
| Make the artifact t-looks share 12 as an agent | PUT /internal/v1/agents/5/gateway/artifacts | 201 | 37.0 | 109 B | pass |
| Create a link to the picture and frame artifact | POST /v1/shares | 201 | 78.0 | 461 B | pass |
| Read the picture blob through the link | GET /artifact/e26342d9933ad0e9822f26846cebee61/blobs/bbaba9aba10ca9576 | 200 | 1.00 | 22 B | pass |
| Refuse a blob the artifact does not name | GET /artifact/e26342d9933ad0e9822f26846cebee61/blobs/b0000000000000000 | 404 | 1.00 | 19 B | pass |
| Refuse a blob name with a slash in it | GET /artifact/e26342d9933ad0e9822f26846cebee61/blobs/x/y | 404 | 1.00 | 19 B | pass |
| Read the HTML panel's page through the link | GET /artifact/e26342d9933ad0e9822f26846cebee61/frame/page/index.html | 200 | 1.00 | 77 B | pass |
| Refuse a frame of a panel that does not exist | GET /artifact/e26342d9933ad0e9822f26846cebee61/frame/nope/index.html | 404 | 1.00 | 21 B | pass |
| Refuse a frame path that climbs out of the panel | GET /artifact/e26342d9933ad0e9822f26846cebee61/frame/page/../secret | 404 | 1.00 | 19 B | pass |
| The owner gets the content URL of an HTML panel | GET /v1/artifacts/adg5djzfccl3/frame?panel=page | 200 | 1.00 | 132 B | pass |
| Refuse the owner's frame URL of a panel that is not HTML | GET /v1/artifacts/adg5djzfccl3/frame?panel=words | 404 | 1.00 | 30 B | pass |
| Refuse the owner's frame URL of an artifact that does not exist | GET /v1/artifacts/aunknownid00/frame?panel=page | 404 | 2.00 | 28 B | pass |
| Refuse a frame version that is not a number | GET /v1/artifacts/adg5djzfccl3/frame?panel=page&v=x | 400 | 0.00 | 38 B | pass |
| Revoke the picture link | DELETE /v1/shares/e26342d9933ad0e9822f26846cebee61 | 200 | 71.0 | 473 B | pass |
| Delete agent 5 again, so later folders do not see it | DELETE /v1/agents/5 | 200 | 1.00 | 34 B | pass |
| The viewer page answers not built while no console is built | GET /artifact/e3147a278fd73c6082d8c9bc747b28c3 | 404 | 1.00 | 35 B | pass |
| The viewer page of a missing link answers the same | GET /artifact/00000000000000000000000000000000 | 404 | 1.00 | 35 B | pass |
| The viewer page of a malformed link answers the same | GET /artifact/not-a-share | 404 | 0.00 | 35 B | pass |
| A viewer asset that does not exist is missing | GET /artifact/_assets/app.js | 404 | 0.00 | 19 B | pass |
| A viewer asset path that climbs out is missing | GET /artifact/_assets/../secret | 404 | 0.00 | 19 B | pass |
| A path under a live link that does not exist is missing | GET /artifact/e3147a278fd73c6082d8c9bc747b28c3/nope | 404 | 0.00 | 28 B | pass |
| Read the appearance (the default look) | GET /v1/appearance | 200 | 1.00 | 112 B | pass |
| Save a chosen built-in look | PUT /v1/appearance | 200 | 0.00 | 115 B | pass |
| Refuse a look that names no known theme | PUT /v1/appearance | 400 | 2.00 | 43 B | pass |
| Save a valid custom theme | PUT /v1/appearance/themes/my-theme | 200 | 1.00 | 250 B | pass |
| Refuse a custom theme that takes a built-in id | PUT /v1/appearance/themes/loom-clay | 400 | 1.00 | 68 B | pass |
| Refuse a theme whose body id differs from the URL | PUT /v1/appearance/themes/other | 400 | 1.00 | 56 B | pass |
| Delete the custom theme | DELETE /v1/appearance/themes/my-theme | 200 | 0.00 | 115 B | pass |
| Put the default look back | PUT /v1/appearance | 200 | 1.00 | 112 B | pass |
| List the artifacts | GET /v1/artifacts | 200 | 1.00 | 1.7 KiB | pass |
| Read an artifact that does not exist | GET /v1/artifacts/aunknownid00 | 404 | 1.00 | 39 B | pass |
| List the share links | GET /v1/shares | 200 | 0.00 | 7.3 KiB | pass |
| Refuse a share link for an artifact that does not exist | POST /v1/shares | 404 | 1.00 | 36 B | pass |
| Read the data of an artifact that does not exist | GET /artifact/0123456789abcdef0123456789abcdef/data | 404 | 0.00 | 19 B | pass |
incidents
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the project's incidents | GET /v1/projects/pr11/incidents | 200 | 1.00 | 16 B | pass |
| An incident the project does not have | GET /v1/projects/pr11/incidents/no-such-incident | 404 | 1.00 | 28 B | pass |
| Resolving an incident the project does not have | POST /v1/projects/pr11/incidents/no-such-incident/resolve | 404 | 0.00 | 28 B | pass |
| List the project's recorded landings | GET /v1/projects/pr11/landings | 200 | 1.00 | 200 B | pass |
tool-library
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| The tool library's top-level groups, and the route groups it leaves out | GET /v1/tool-library | 200 | 4.00 | 10.4 KiB | pass |
| A group's children: the deploy entries, with their kind and summary | GET /v1/tool-library?path=deploy | 200 | 2.00 | 722 B | pass |
| Search the tool library by words: rollback finds the deploy and the Kubernetes undo | GET /v1/tool-library/search?q=rollback | 200 | 3.00 | 549 B | pass |
| One entry: its schema, its policy, and the seats that may use it | GET /v1/tool-library/entry?path=deploy.rollback | 200 | 3.00 | 2.6 KiB | pass |
ephemeral
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| The ephemeral instances of the project are listed | GET /v1/projects/pr6/ephemeral | 200 | 1.00 | 2 B | pass |
| Refuse an instance of an environment that is not ephemeral | POST /v1/projects/pr6/ephemeral | 409 | 1.00 | 36 B | pass |
| Refuse a start that names no subject | POST /v1/projects/pr6/ephemeral | 400 | 1.00 | 28 B | pass |
| Refuse an instance that is not there | GET /v1/ephemeral/eph-probe-missing | 404 | 2.00 | 28 B | pass |
| Refuse to tear down an instance that is not there | DELETE /v1/ephemeral/eph-probe-missing | 404 | 0.00 | 41 B | pass |
| Refuse the instances of a project that does not exist | GET /v1/projects/no-such-project/ephemeral | 404 | 1.00 | 27 B | pass |
| Check a project that does not exist is not found | POST /v1/projects/no-such-project/ephemeral/against | 404 | 1.00 | 27 B | pass |
internal
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Refuse the hello without the internal token | POST /internal/v1/agents/61003/hello | 403 | 1.00 | 43 B | pass |
| Refuse the message without the internal token | POST /internal/v1/agents/61003/message | 403 | 1.00 | 43 B | pass |
| Refuse the gone without the internal token | POST /internal/v1/agents/61003/gone | 403 | 0.00 | 43 B | pass |
| Refuse the egress without the internal token | POST /internal/v1/agents/61003/egress | 403 | 1.00 | 43 B | pass |
| Refuse the egress check without the internal token | POST /internal/egress/check | 403 | 1.00 | 43 B | pass |
| Refuse the egress cert without the internal token | POST /internal/egress/cert | 403 | 1.00 | 43 B | pass |
| Refuse the egress inject without the internal token | POST /internal/egress/inject | 403 | 1.00 | 43 B | pass |
| Refuse the decisions without the internal token | POST /internal/v1/agents/61003/gateway/decisions | 403 | 1.00 | 43 B | pass |
| Refuse the e2b sandbox without the internal token | GET /internal/e2b/sandboxes/sb-t-internal-none | 403 | 1.00 | 43 B | pass |
| Refuse the anthropic messages without the internal token | POST /internal/v1/agents/61003/gateway/anthropic/v1/messages | 403 | 1.00 | 43 B | pass |
| Refuse the anthropic socket without the internal token | GET /internal/v1/agents/61003/gateway/anthropic/v1/messages | 403 | 0.00 | 43 B | pass |
| Refuse the anthropic models without the internal token | GET /internal/v1/agents/61003/gateway/anthropic/v1/models | 403 | 1.00 | 43 B | pass |
| Refuse the openai responses without the internal token | POST /internal/v1/agents/61003/gateway/openai/v1/responses | 403 | 1.00 | 43 B | pass |
| Refuse the openai responses socket without the internal token | GET /internal/v1/agents/61003/gateway/openai/v1/responses | 403 | 0.00 | 43 B | pass |
| Refuse the openai chat without the internal token | POST /internal/v1/agents/61003/gateway/openai/v1/chat/completions | 403 | 1.00 | 43 B | pass |
| Refuse the openai models without the internal token | GET /internal/v1/agents/61003/gateway/openai/v1/models | 403 | 2.00 | 43 B | pass |
| Refuse the openrouter chat without the internal token | POST /internal/v1/agents/61003/gateway/openrouter/v1/chat/completions | 403 | 1.00 | 43 B | pass |
| Refuse the openrouter messages without the internal token | POST /internal/v1/agents/61003/gateway/openrouter/v1/messages | 403 | 1.00 | 43 B | pass |
| Refuse the openrouter models without the internal token | GET /internal/v1/agents/61003/gateway/openrouter/v1/models | 403 | 1.00 | 43 B | pass |
| Refuse the opencode chat without the internal token | POST /internal/v1/agents/61003/gateway/opencode/v1/chat/completions | 403 | 1.00 | 43 B | pass |
| Refuse the opencode responses without the internal token | POST /internal/v1/agents/61003/gateway/opencode/v1/responses | 403 | 1.00 | 43 B | pass |
| Refuse the opencode messages without the internal token | POST /internal/v1/agents/61003/gateway/opencode/v1/messages | 403 | 1.00 | 43 B | pass |
| Refuse the opencode models without the internal token | GET /internal/v1/agents/61003/gateway/opencode/v1/models | 403 | 0.00 | 43 B | pass |
| Refuse the gemini call without the internal token | POST /internal/v1/agents/61003/gateway/gemini/v1beta/models/gemini-x:generateContent | 403 | 0.00 | 43 B | pass |
| Refuse the gemini models without the internal token | GET /internal/v1/agents/61003/gateway/gemini/v1beta/models | 403 | 1.00 | 43 B | pass |
| Refuse the vendor read without the internal token | GET /internal/v1/agents/61003/gateway/vendor/augment/v1/sessions | 403 | 2.00 | 43 B | pass |
| Refuse the vendor call without the internal token | POST /internal/v1/agents/61003/gateway/vendor/augment/v1/sessions | 403 | 0.00 | 43 B | pass |
| Refuse the file upload without the internal token | PUT /internal/v1/agents/61003/gateway/files?name=t-internal.txt | 403 | 1.00 | 43 B | pass |
| Refuse the file download without the internal token | GET /internal/v1/agents/61003/gateway/files/fb-t-internal-none | 403 | 0.00 | 43 B | pass |
| Refuse the artifact create without the internal token | PUT /internal/v1/agents/61003/gateway/artifacts | 403 | 1.00 | 43 B | pass |
| Refuse the blob upload without the internal token | PUT /internal/v1/agents/61003/gateway/artifacts/blobs?content_type=text/plain | 403 | 1.00 | 43 B | pass |
| Refuse the artifact version without the internal token | PUT /internal/v1/agents/61003/gateway/artifacts/art-t-internal-none | 403 | 1.00 | 43 B | pass |
| Refuse the artifact read without the internal token | GET /internal/v1/agents/61003/gateway/artifacts/art-t-internal-none | 403 | 0.00 | 43 B | pass |
| Refuse the artifact share without the internal token | POST /internal/v1/agents/61003/gateway/artifacts/art-t-internal-none/share | 403 | 0.00 | 43 B | pass |
| Refuse the weft read without the internal token | GET /internal/v1/agents/61003/gateway/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 403 | 0.00 | 43 B | pass |
| Refuse the weft write without the internal token | POST /internal/v1/agents/61003/gateway/weft/wire/want | 403 | 2.00 | 43 B | pass |
| Refuse the weft upload without the internal token | PUT /internal/v1/agents/61003/gateway/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 403 | 0.00 | 43 B | pass |
| Refuse a node cache's node weft read without a node signature | GET /internal/v1/nodes/1/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 403 | 13.0 | 30 B | pass |
| Refuse a node cache's node weft batch without a node signature | POST /internal/v1/nodes/1/weft/wire/want | 403 | 1.00 | 30 B | pass |
| Refuse a node cache's node weft upload without a node signature | PUT /internal/v1/nodes/1/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 403 | 1.00 | 30 B | pass |
| Refuse the lease open without the internal token | POST /internal/v1/agents/61003/stations/s-t-internal-none/leases | 403 | 1.00 | 43 B | pass |
| Refuse the lease release without the internal token | DELETE /internal/v1/agents/61003/stations/s-t-internal-none/leases/l-t-internal-none | 403 | 0.00 | 43 B | pass |
| Refuse the hello with a wrong internal token | POST /internal/v1/agents/61003/hello | 403 | 1.00 | 43 B | pass |
| Refuse the message with a wrong internal token | POST /internal/v1/agents/61003/message | 403 | 1.00 | 43 B | pass |
| Refuse the gone with a wrong internal token | POST /internal/v1/agents/61003/gone | 403 | 0.00 | 43 B | pass |
| Refuse the egress with a wrong internal token | POST /internal/v1/agents/61003/egress | 403 | 0.00 | 43 B | pass |
| Refuse the egress check with a wrong internal token | POST /internal/egress/check | 403 | 1.00 | 43 B | pass |
| Refuse the egress cert with a wrong internal token | POST /internal/egress/cert | 403 | 1.00 | 43 B | pass |
| Refuse the egress inject with a wrong internal token | POST /internal/egress/inject | 403 | 1.00 | 43 B | pass |
| Refuse the decisions with a wrong internal token | POST /internal/v1/agents/61003/gateway/decisions | 403 | 1.00 | 43 B | pass |
| Refuse the e2b sandbox with a wrong internal token | GET /internal/e2b/sandboxes/sb-t-internal-none | 403 | 1.00 | 43 B | pass |
| Refuse the anthropic messages with a wrong internal token | POST /internal/v1/agents/61003/gateway/anthropic/v1/messages | 403 | 1.00 | 43 B | pass |
| Refuse the anthropic socket with a wrong internal token | GET /internal/v1/agents/61003/gateway/anthropic/v1/messages | 403 | 0.00 | 43 B | pass |
| Refuse the anthropic models with a wrong internal token | GET /internal/v1/agents/61003/gateway/anthropic/v1/models | 403 | 0.00 | 43 B | pass |
| Refuse the openai responses with a wrong internal token | POST /internal/v1/agents/61003/gateway/openai/v1/responses | 403 | 1.00 | 43 B | pass |
| Refuse the openai responses socket with a wrong internal token | GET /internal/v1/agents/61003/gateway/openai/v1/responses | 403 | 3.00 | 43 B | pass |
| Refuse the openai chat with a wrong internal token | POST /internal/v1/agents/61003/gateway/openai/v1/chat/completions | 403 | 1.00 | 43 B | pass |
| Refuse the openai models with a wrong internal token | GET /internal/v1/agents/61003/gateway/openai/v1/models | 403 | 1.00 | 43 B | pass |
| Refuse the openrouter chat with a wrong internal token | POST /internal/v1/agents/61003/gateway/openrouter/v1/chat/completions | 403 | 0.00 | 43 B | pass |
| Refuse the openrouter messages with a wrong internal token | POST /internal/v1/agents/61003/gateway/openrouter/v1/messages | 403 | 1.00 | 43 B | pass |
| Refuse the openrouter models with a wrong internal token | GET /internal/v1/agents/61003/gateway/openrouter/v1/models | 403 | 1.00 | 43 B | pass |
| Refuse the opencode chat with a wrong internal token | POST /internal/v1/agents/61003/gateway/opencode/v1/chat/completions | 403 | 1.00 | 43 B | pass |
| Refuse the opencode responses with a wrong internal token | POST /internal/v1/agents/61003/gateway/opencode/v1/responses | 403 | 1.00 | 43 B | pass |
| Refuse the opencode messages with a wrong internal token | POST /internal/v1/agents/61003/gateway/opencode/v1/messages | 403 | 0.00 | 43 B | pass |
| Refuse the opencode models with a wrong internal token | GET /internal/v1/agents/61003/gateway/opencode/v1/models | 403 | 2.00 | 43 B | pass |
| Refuse the gemini call with a wrong internal token | POST /internal/v1/agents/61003/gateway/gemini/v1beta/models/gemini-x:generateContent | 403 | 1.00 | 43 B | pass |
| Refuse the gemini models with a wrong internal token | GET /internal/v1/agents/61003/gateway/gemini/v1beta/models | 403 | 1.00 | 43 B | pass |
| Refuse the vendor read with a wrong internal token | GET /internal/v1/agents/61003/gateway/vendor/augment/v1/sessions | 403 | 1.00 | 43 B | pass |
| Refuse the vendor call with a wrong internal token | POST /internal/v1/agents/61003/gateway/vendor/augment/v1/sessions | 403 | 1.00 | 43 B | pass |
| Refuse the file upload with a wrong internal token | PUT /internal/v1/agents/61003/gateway/files?name=t-internal.txt | 403 | 1.00 | 43 B | pass |
| Refuse the file download with a wrong internal token | GET /internal/v1/agents/61003/gateway/files/fb-t-internal-none | 403 | 2.00 | 43 B | pass |
| Refuse the artifact create with a wrong internal token | PUT /internal/v1/agents/61003/gateway/artifacts | 403 | 1.00 | 43 B | pass |
| Refuse the blob upload with a wrong internal token | PUT /internal/v1/agents/61003/gateway/artifacts/blobs?content_type=text/plain | 403 | 1.00 | 43 B | pass |
| Refuse the artifact version with a wrong internal token | PUT /internal/v1/agents/61003/gateway/artifacts/art-t-internal-none | 403 | 2.00 | 43 B | pass |
| Refuse the artifact read with a wrong internal token | GET /internal/v1/agents/61003/gateway/artifacts/art-t-internal-none | 403 | 1.00 | 43 B | pass |
| Refuse the artifact share with a wrong internal token | POST /internal/v1/agents/61003/gateway/artifacts/art-t-internal-none/share | 403 | 1.00 | 43 B | pass |
| Refuse the weft read with a wrong internal token | GET /internal/v1/agents/61003/gateway/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 403 | 0.00 | 43 B | pass |
| Refuse the weft write with a wrong internal token | POST /internal/v1/agents/61003/gateway/weft/wire/want | 403 | 1.00 | 43 B | pass |
| Refuse the weft upload with a wrong internal token | PUT /internal/v1/agents/61003/gateway/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 403 | 1.00 | 43 B | pass |
| Refuse a node cache's node weft read with an internal token, no node signature | GET /internal/v1/nodes/1/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 403 | 1.00 | 30 B | pass |
| Refuse a node cache's node weft batch with an internal token, no node signature | POST /internal/v1/nodes/1/weft/wire/want | 403 | 1.00 | 30 B | pass |
| Refuse a node cache's node weft upload with an internal token, no node signature | PUT /internal/v1/nodes/1/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 403 | 1.00 | 30 B | pass |
| Refuse the lease open with a wrong internal token | POST /internal/v1/agents/61003/stations/s-t-internal-none/leases | 403 | 2.00 | 43 B | pass |
| Refuse the lease release with a wrong internal token | DELETE /internal/v1/agents/61003/stations/s-t-internal-none/leases/l-t-internal-none | 403 | 1.00 | 43 B | pass |
| Answer 404 for an e2b sandbox the hub does not know | GET /internal/e2b/sandboxes/sb-t-internal-none | 404 | 1.00 | 27 B | pass |
| Refuse a hello whose CID is not a number | POST /internal/v1/agents/abc/hello | 400 | 0.00 | 36 B | pass |
| Refuse a hello whose CID is past the 64-bit range | POST /internal/v1/agents/99999999999999999999999/hello | 400 | 1.00 | 36 B | pass |
| Refuse a guest message with a malformed body | POST /internal/v1/agents/61001/message | 400 | 1.00 | 106 B | pass |
| Refuse a guest message with an unknown type | POST /internal/v1/agents/61001/message | 400 | 1.00 | 267 B | pass |
| Refuse a tool request from an agent the hub never heard of | POST /internal/v1/agents/61001/message | 200 | 1.00 | 105 B | pass |
| Answer a model call from an agent the hub never heard of with an error | POST /internal/v1/agents/61001/message | 200 | 1.00 | 76 B | pass |
| Ignore a tool report from an agent the hub never heard of | POST /internal/v1/agents/61001/message | 200 | 2.00 | 14 B | pass |
| Ignore a work report from an agent the hub never heard of | POST /internal/v1/agents/61001/message | 200 | 1.00 | 14 B | pass |
| Ignore a heartbeat from an agent the hub never heard of | POST /internal/v1/agents/61001/message | 200 | 1.00 | 14 B | pass |
| Ignore a compaction notice from an agent the hub never heard of | POST /internal/v1/agents/61001/message | 200 | 0.00 | 14 B | pass |
| Ignore a stats report from an agent the hub never heard of | POST /internal/v1/agents/61001/message | 200 | 1.00 | 14 B | pass |
| Ignore an engine update from an agent the hub never heard of | POST /internal/v1/agents/61001/message | 200 | 1.00 | 14 B | pass |
| Ignore a bye from an agent the hub never heard of | POST /internal/v1/agents/61001/message | 200 | 1.00 | 14 B | pass |
| Accept a gone for an agent the hub never heard of | POST /internal/v1/agents/61001/gone | 200 | 1.00 | 2 B | pass |
| Refuse an egress check for an agent the hub never heard of | POST /internal/egress/check | 200 | 1.00 | 69 B | pass |
| Refuse a Messages call for an agent the hub never heard of | POST /internal/v1/agents/61001/gateway/anthropic/v1/messages | 404 | 2.00 | 25 B | pass |
| Refuse a model list for an agent the hub never heard of | GET /internal/v1/agents/61001/gateway/openai/v1/models | 404 | 1.00 | 25 B | pass |
| Refuse a decision for an agent the hub never heard of | POST /internal/v1/agents/61001/gateway/decisions | 404 | 0.00 | 25 B | pass |
| Refuse a vendor read for an agent the hub never heard of | GET /internal/v1/agents/61001/gateway/vendor/augment/v1/sessions | 404 | 0.00 | 25 B | pass |
| Refuse a Weft statement for an agent the hub never heard of | POST /internal/v1/agents/61001/gateway/weft/records/unsigned | 409 | 1.00 | 26 B | pass |
| Refuse an egress certificate for a host that is not intercepted | POST /internal/egress/cert | 403 | 1.00 | 55 B | pass |
| Refuse a file upload from an agent the hub never heard of | PUT /internal/v1/agents/61001/gateway/files?name=t-ghost-file.txt | 404 | 0.00 | 25 B | pass |
| Refuse a blob upload from an agent the hub never heard of | PUT /internal/v1/agents/61001/gateway/artifacts/blobs?content_type=text/plain | 404 | 1.00 | 25 B | pass |
| Register agent 61002 with the hub | POST /internal/v1/agents/61002/hello | 200 | 4.00 | 55.0 KiB | pass |
| Read agent 61002 back | GET /v1/agents/61002 | 200 | 1.00 | 395 B | pass |
| Ignore a heartbeat from an agent with no session | POST /internal/v1/agents/61002/message | 200 | 1.00 | 14 B | pass |
| Re-register the agent with no project with the hub and keep its profile | POST /internal/v1/agents/61002/hello | 200 | 2.00 | 55.0 KiB | pass |
| Delete agent 61002 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61002 | 200 | 0.00 | 34 B | pass |
| Create a project for t-internal on a project ü 漢字 | POST /v1/projects | 201 | 11.0 | 725 B | pass |
| Register agent 61003 with the hub | POST /internal/v1/agents/61003/hello | 200 | 4.00 | 55.0 KiB | pass |
| Read agent 61003 back | GET /v1/agents/61003 | 200 | 3.00 | 395 B | pass |
| Answer the read tool request as allow | POST /internal/v1/agents/61003/message | 200 | 1.00 | 94 B | pass |
| Allow an edit in the default autonomous mode | POST /internal/v1/agents/61003/message | 200 | 1.00 | 94 B | pass |
| Refuse a tool no law permits | POST /internal/v1/agents/61003/message | 200 | 1.00 | 142 B | pass |
| Accept a tool report for a tool the agent ran | POST /internal/v1/agents/61003/message | 200 | 0.00 | 14 B | pass |
| Record a compaction of the agent's conversation | POST /internal/v1/agents/61003/message | 200 | 1.00 | 14 B | pass |
| Record stats for an agent with no run | POST /internal/v1/agents/61003/message | 200 | 1.00 | 14 B | pass |
| Record an engine update under a work item | POST /internal/v1/agents/61003/message | 200 | 1.00 | 14 B | pass |
| Ignore a work report for work the agent is not doing | POST /internal/v1/agents/61003/message | 200 | 1.00 | 14 B | pass |
| Ignore a heartbeat from an agent on a project | POST /internal/v1/agents/61003/message | 200 | 3.00 | 14 B | pass |
| Refuse a message whose tool request has a non-string call id | POST /internal/v1/agents/61003/message | 400 | 1.00 | 56 B | pass |
| Refuse a message whose tool input is missing | POST /internal/v1/agents/61003/message | 400 | 1.00 | 33 B | pass |
| Refuse a Responses call through OpenCode for a model that is not a Responses model | POST /internal/v1/agents/61003/gateway/opencode/v1/responses | 400 | 0.00 | 118 B | pass |
| Refuse a Messages call through OpenCode for a model OpenCode serves on chat | POST /internal/v1/agents/61003/gateway/opencode/v1/messages | 400 | 1.00 | 132 B | pass |
| List the Anthropic models for the agent | GET /internal/v1/agents/61003/gateway/anthropic/v1/models | 200 | 1.00 | 233 B | pass |
| List the OpenAI models for the agent | GET /internal/v1/agents/61003/gateway/openai/v1/models | 200 | 0.00 | 143 B | pass |
| List the OpenRouter models for the agent | GET /internal/v1/agents/61003/gateway/openrouter/v1/models | 200 | 1.00 | 27 B | pass |
| List the OpenCode models for the agent | GET /internal/v1/agents/61003/gateway/opencode/v1/models | 200 | 1.00 | 27 B | pass |
| List the Gemini models for the agent | GET /internal/v1/agents/61003/gateway/gemini/v1beta/models | 200 | 1.00 | 13 B | pass |
| Refuse a Messages socket for an agent when the provider has no key | GET /internal/v1/agents/61003/gateway/anthropic/v1/messages | 503 | 47.0 | 47 B | pass |
| Refuse a Responses socket for an agent when the provider has no key | GET /internal/v1/agents/61003/gateway/openai/v1/responses | 503 | 25.0 | 44 B | pass |
| Refuse a plain GET on a socket route that only accepts an upgrade | GET /internal/v1/agents/61003/gateway/anthropic/v1/messages | 405 | 1.00 | 38 B | pass |
| Refuse a decision with no decision model | POST /internal/v1/agents/61003/gateway/decisions | 400 | 1.00 | 106 B | pass |
| Refuse a decision whose body lacks the input | POST /internal/v1/agents/61003/gateway/decisions | 400 | 1.00 | 62 B | pass |
| Refuse a vendor read while the Auggie engine is off | GET /internal/v1/agents/61003/gateway/vendor/augment/v1/sessions | 403 | 2.00 | 86 B | pass |
| Refuse a vendor call with a vendor that does not exist | POST /internal/v1/agents/61003/gateway/vendor/nosuch/v1/x | 404 | 1.00 | 26 B | pass |
| Refuse a vendor path that is not a plain path | GET /internal/v1/agents/61003/gateway/vendor/augment/../secret | 400 | 1.00 | 47 B | pass |
| Refuse a vendor write while the Auggie engine is off | POST /internal/v1/agents/61003/gateway/vendor/augment/v1/sessions | 403 | 1.00 | 86 B | pass |
| Store a file the agent hands in | PUT /internal/v1/agents/61003/gateway/files?name=t-internal-note.txt&title=t-internal%20note | 201 | 38.0 | 122 B | pass |
| Refuse a file upload without a name | PUT /internal/v1/agents/61003/gateway/files | 400 | 1.00 | 28 B | pass |
| Let the agent download the file it stored | GET /internal/v1/agents/61003/gateway/files/fd8c724c0d660 | 200 | 0.00 | 29 B | pass |
| Answer 404 for a file the agent cannot read | GET /internal/v1/agents/61003/gateway/files/fb-t-internal-none | 404 | 1.00 | 24 B | pass |
| Store a data blob an artifact reads | PUT /internal/v1/agents/61003/gateway/artifacts/blobs?content_type=text/plain | 201 | 14.0 | 73 B | pass |
| Refuse an artifact with no panels | PUT /internal/v1/agents/61003/gateway/artifacts | 400 | 18.0 | 105 B | pass |
| Refuse an artifact whose panel span is not a grid span | PUT /internal/v1/agents/61003/gateway/artifacts | 400 | 17.0 | 96 B | pass |
| Refuse an artifact without a spec | PUT /internal/v1/agents/61003/gateway/artifacts | 400 | 1.00 | 51 B | pass |
| Create an artifact on the project | PUT /internal/v1/agents/61003/gateway/artifacts | 201 | 42.0 | 111 B | pass |
| Read the artifact back | GET /internal/v1/agents/61003/gateway/artifacts/a7vzrv6hfia7 | 200 | 1.00 | 330 B | pass |
| Add a version to the artifact | PUT /internal/v1/agents/61003/gateway/artifacts/a7vzrv6hfia7 | 201 | 22.0 | 116 B | pass |
| Read an older version of the artifact | GET /internal/v1/agents/61003/gateway/artifacts/a7vzrv6hfia7?v=1 | 200 | 1.00 | 330 B | pass |
| Answer 404 for a version the artifact does not have | GET /internal/v1/agents/61003/gateway/artifacts/a7vzrv6hfia7?v=9 | 404 | 1.00 | 28 B | pass |
| Ask the owner to share the artifact | POST /internal/v1/agents/61003/gateway/artifacts/a7vzrv6hfia7/share | 202 | 1.00 | 35 B | pass |
| Refuse a share request with an empty reason | POST /internal/v1/agents/61003/gateway/artifacts/a7vzrv6hfia7/share | 400 | 0.00 | 76 B | pass |
| Read the share request on the owner's approvals | GET /v1/approvals | 200 | 0.00 | 640 B | pass |
| Owner declines the share request | POST /v1/approvals/a1 | 200 | 17.0 | 654 B | pass |
| Answer 404 for a Weft object the hub does not hold | GET /internal/v1/agents/61003/gateway/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 404 | 1.00 | 26 B | pass |
| Refuse a Weft object whose bytes do not match its hash | PUT /internal/v1/agents/61003/gateway/weft/wire/objects/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 400 | 2.00 | 60 B | pass |
| Refuse a Weft info without hashes | POST /internal/v1/agents/61003/gateway/weft/wire/info | 400 | 1.00 | 29 B | pass |
| Refuse a Weft want for a revision that does not exist | POST /internal/v1/agents/61003/gateway/weft/wire/want | 404 | 1.00 | 39 B | pass |
| Refuse a Weft statement the agent does not say | POST /internal/v1/agents/61003/gateway/weft/records/unsigned | 403 | 1.00 | 48 B | pass |
| Refuse an agent's attest without its revision | POST /internal/v1/agents/61003/gateway/weft/records/unsigned | 409 | 30.0 | 50 B | pass |
| Refuse an agent the hub's own Weft records route | POST /internal/v1/agents/61003/gateway/weft/records | 403 | 1.00 | 35 B | pass |
| Refuse an agent the hub's Weft lanes | GET /internal/v1/agents/61003/gateway/weft/lanes | 403 | 1.00 | 35 B | pass |
| Allow an agent a connection the laws permit | POST /internal/egress/check | 200 | 1.00 | 57 B | pass |
| Record an outbound connection of the agent | POST /internal/v1/agents/61003/egress | 200 | 1.00 | 2 B | pass |
| Refuse a certificate for a host that is not intercepted | POST /internal/egress/cert | 403 | 1.00 | 55 B | pass |
| Answer an egress inject with no matching rule | POST /internal/egress/inject | 200 | 0.00 | 2 B | pass |
| Refuse an egress check whose body is malformed | POST /internal/egress/check | 400 | 1.00 | 89 B | pass |
| Refuse an egress check whose CID is text | POST /internal/egress/check | 400 | 1.00 | 72 B | pass |
| Read the settings before the fetch blocklist change | GET /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| Block a host on the fetch blocklist | PUT /v1/settings | 200 | 6.00 | 1.2 KiB | pass |
| Refuse a connection to a host on the fetch blocklist | POST /internal/egress/check | 200 | 1.00 | 114 B | pass |
| Refuse a connection to a subdomain of a blocked host | POST /internal/egress/check | 200 | 1.00 | 118 B | pass |
| Restore the fetch blocklist | PUT /v1/settings | 200 | 6.00 | 1.2 KiB | pass |
| Allow the host again once the blocklist is restored | POST /internal/egress/check | 200 | 2.00 | 57 B | pass |
| Refuse a lease on a workstation that does not exist | POST /internal/v1/agents/61003/stations/s-t-internal-none/leases | 404 | 1.00 | 31 B | pass |
| Refuse a lease with an unknown kind | POST /internal/v1/agents/61003/stations/s-t-internal-none/leases | 400 | 1.00 | 126 B | pass |
| Refuse a lease without a kind | POST /internal/v1/agents/61003/stations/s-t-internal-none/leases | 400 | 1.00 | 61 B | pass |
| Answer 404 for releasing a lease that does not exist | DELETE /internal/v1/agents/61003/stations/s-t-internal-none/leases/l-t-internal-none | 404 | 1.00 | 25 B | pass |
| Read agent 61003 back | GET /v1/agents/61003 | 200 | 0.00 | 448 B | pass |
| Delete agent 61003 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61003 | 200 | 1.00 | 34 B | pass |
| Create a project for t-internal approval approved | POST /v1/projects | 201 | 20.0 | 725 B | pass |
| Register agent 61004 with the hub | POST /internal/v1/agents/61004/hello | 200 | 3.00 | 55.0 KiB | pass |
| Set the agent to ask before a write | PUT /v1/agents/61004/permissions | 200 | 50.0 | 27 B | pass |
| Read the agent's permission mode | GET /v1/agents/61004/permissions | 200 | 1.00 | 31 B | pass |
| Hold a write until the owner approves it | POST /internal/v1/agents/61004/message | 200 | 1.00 | 195 B | pass |
| The owner sees the held write as pending | GET /v1/approvals | 200 | 1.00 | 911 B | pass |
| Owner approves the held write | POST /v1/approvals/a2 | 200 | 25.0 | 255 B | pass |
| Run the approved write when the agent runs it again | POST /internal/v1/agents/61004/message | 200 | 2.00 | 95 B | pass |
| Hold the same write again once its approval is used | POST /internal/v1/agents/61004/message | 200 | 2.00 | 195 B | pass |
| The used approval reads back as used | GET /v1/approvals | 200 | 1.00 | 1.1 KiB | pass |
| Refuse to decide an approval that is already used | POST /v1/approvals/a2 | 409 | 0.00 | 44 B | pass |
| Owner declines the second held write | POST /v1/approvals/a3 | 200 | 29.0 | 261 B | pass |
| Answer 404 for an approval the hub does not hold | POST /v1/approvals/a-t-internal-none | 404 | 0.00 | 28 B | pass |
| Delete agent 61004 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61004 | 200 | 0.00 | 34 B | pass |
| Create a project for t-internal approval denied | POST /v1/projects | 201 | 16.0 | 717 B | pass |
| Register agent 61005 with the hub | POST /internal/v1/agents/61005/hello | 200 | 3.00 | 55.0 KiB | pass |
| Set the agent to ask before a command | PUT /v1/agents/61005/permissions | 200 | 33.0 | 27 B | pass |
| Hold a command until the owner decides | POST /internal/v1/agents/61005/message | 200 | 2.00 | 195 B | pass |
| Owner declines the held command | POST /v1/approvals/a4 | 200 | 114 | 187 B | pass |
| The declined command reads back as denied | GET /v1/approvals | 200 | 1.00 | 1.3 KiB | pass |
| Refuse the declined command again until the owner decides a new hold | POST /internal/v1/agents/61005/message | 200 | 1.00 | 195 B | pass |
| Delete agent 61005 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61005 | 200 | 0.00 | 34 B | pass |
| Create a project for t-internal read only | POST /v1/projects | 201 | 10.0 | 693 B | pass |
| Register agent 61006 with the hub | POST /internal/v1/agents/61006/hello | 200 | 2.00 | 55.0 KiB | pass |
| Set the agent to read only | PUT /v1/agents/61006/permissions | 200 | 33.0 | 33 B | pass |
| Answer the edit tool request as deny | POST /internal/v1/agents/61006/message | 200 | 1.00 | 136 B | pass |
| Answer the bash tool request as deny | POST /internal/v1/agents/61006/message | 200 | 1.00 | 135 B | pass |
| Allow a read in read-only mode | POST /internal/v1/agents/61006/message | 200 | 3.00 | 94 B | pass |
| Read the agent's permission mode back | GET /v1/agents/61006/permissions | 200 | 1.00 | 37 B | pass |
| Delete agent 61006 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61006 | 200 | 1.00 | 34 B | pass |
| Create a project for t-internal engine approval | POST /v1/projects | 201 | 14.0 | 717 B | pass |
| Register agent 61007 with the hub | POST /internal/v1/agents/61007/hello | 200 | 5.00 | 55.0 KiB | pass |
| Refuse an engine request that is not a known tool | POST /internal/v1/agents/61007/message | 200 | 1.00 | 142 B | pass |
| Set the agent to ask before an engine request | PUT /v1/agents/61007/permissions | 200 | 10.0 | 27 B | pass |
| Hold an ACP engine request for the owner | POST /internal/v1/agents/61007/message | 200 | 3.00 | 149 B | pass |
| Owner approves the held engine request | POST /v1/approvals/a6 | 200 | 9.00 | 242 B | pass |
| Run the approved engine request when the engine asks again | POST /internal/v1/agents/61007/message | 200 | 1.00 | 94 B | pass |
| Set the harness to one without ACP support | POST /internal/v1/agents/61007/hello | 200 | 2.00 | 54.8 KiB | pass |
| Refuse an engine request from a harness without ACP support | POST /internal/v1/agents/61007/message | 200 | 1.00 | 194 B | pass |
| Delete agent 61007 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61007 | 200 | 1.00 | 34 B | pass |
| Create a project for t-internal stop all | POST /v1/projects | 201 | 10.0 | 689 B | pass |
| Register agent 61008 with the hub | POST /internal/v1/agents/61008/hello | 200 | 3.00 | 55.0 KiB | pass |
| Stop the plaza | POST /v1/plaza/stop | 200 | 2.00 | 79 B | pass |
| Hold a model call while the plaza is stopped | POST /internal/v1/agents/61008/message | 200 | 1.00 | 132 B | pass |
| Hold a Messages call while the plaza is stopped | POST /internal/v1/agents/61008/gateway/anthropic/v1/messages | 409 | 1.00 | 73 B | pass |
| Hold a Weft write while the plaza is stopped | POST /internal/v1/agents/61008/gateway/weft/records/unsigned | 423 | 0.00 | 48 B | pass |
| Hold a vendor read while the plaza is stopped | GET /internal/v1/agents/61008/gateway/vendor/augment/v1/sessions | 409 | 1.00 | 73 B | pass |
| Resume the plaza | POST /v1/plaza/resume | 200 | 3.00 | 80 B | pass |
| Answer the model call again once the plaza resumes | POST /internal/v1/agents/61008/message | 200 | 13.0 | 146 B | pass |
| Delete agent 61008 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61008 | 200 | 1.00 | 34 B | pass |
| Create a project for t-internal paused project | POST /v1/projects | 201 | 21.0 | 713 B | pass |
| Register agent 61009 with the hub | POST /internal/v1/agents/61009/hello | 200 | 3.00 | 55.0 KiB | pass |
| Pause the project | POST /v1/projects/pr18/pause | 200 | 1.00 | 786 B | pass |
| Read agent 61009 back | GET /v1/agents/61009 | 200 | 1.00 | 395 B | pass |
| Answer the read tool request as allow: no session, so the pause holds nothing | POST /internal/v1/agents/61009/message | 200 | 1.00 | 95 B | pass |
| Take the owner's chat with an agent with no session: the pause holds nothing | POST /v1/agents/61009/chat | 200 | 1.00 | 10 B | pass |
| Resume the project | POST /v1/projects/pr18/resume | 200 | 2.00 | 862 B | pass |
| Allow a tool again once the project resumes | POST /internal/v1/agents/61009/message | 200 | 3.00 | 95 B | pass |
| Delete agent 61009 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61009 | 200 | 1.00 | 34 B | pass |
| Create a project for t-internal gone and back | POST /v1/projects | 201 | 29.0 | 709 B | pass |
| Register agent 61010 with the hub | POST /internal/v1/agents/61010/hello | 200 | 3.00 | 55.0 KiB | pass |
| Allow a tool while the session is up | POST /internal/v1/agents/61010/message | 200 | 1.00 | 94 B | pass |
| The agent's session ends | POST /internal/v1/agents/61010/gone | 200 | 0.00 | 2 B | pass |
| Read agent 61010 back | GET /v1/agents/61010 | 200 | 1.00 | 449 B | pass |
| Refuse the owner's chat with an offline agent | POST /v1/agents/61010/chat | 409 | 2.00 | 28 B | pass |
| A second session end leaves the agent offline | POST /internal/v1/agents/61010/gone | 200 | 1.00 | 2 B | pass |
| Read agent 61010 back | GET /v1/agents/61010 | 200 | 1.00 | 449 B | pass |
| The agent registers again | POST /internal/v1/agents/61010/hello | 200 | 2.00 | 55.0 KiB | pass |
| Read agent 61010 back | GET /v1/agents/61010 | 200 | 0.00 | 446 B | pass |
| Delete agent 61010 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61010 | 200 | 1.00 | 34 B | pass |
| Create a project for t-internal queued work | POST /v1/projects | 201 | 21.0 | 701 B | pass |
| Register agent 61011 with the hub | POST /internal/v1/agents/61011/hello | 200 | 4.00 | 55.0 KiB | pass |
| Queue work for the agent | POST /v1/agents/61011/chat | 200 | 1.00 | 10 B | pass |
| The queued work waits: the agent has no work in progress | GET /v1/agents/61011 | 200 | 1.00 | 395 B | pass |
| Ignore a report for work the agent never started | POST /internal/v1/agents/61011/message | 200 | 1.00 | 14 B | pass |
| Cancel does not reach queued work: nothing is in flight | POST /v1/agents/61011/cancel | 200 | 1.00 | 19 B | pass |
| The queued work still waits after the cancel | GET /v1/agents/61011 | 200 | 1.00 | 444 B | pass |
| Delete agent 61011 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/61011 | 200 | 1.00 | 34 B | pass |
stations
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Issue a join code to try a workstation join with | POST /v1/stations/codes | 201 | 13.0 | 221 B | pass |
| Refuse a join from the hub's own host even with a live code | POST /v1/stations/join | 403 | 1.00 | 95 B | pass |
| List the join codes after a refused join | GET /v1/stations/codes | 200 | 2.00 | 125 B | pass |
| Answer 400 to a workstation join whose body does not parse | POST /v1/stations/join | 400 | 1.00 | 109 B | pass |
| Refuse a workstation join that names no join code | POST /v1/stations/join | 400 | 1.00 | 62 B | pass |
| Revoke the unused join code | DELETE /v1/stations/codes/j1 | 200 | 16.0 | 26 B | pass |
| Revoke a join code a second time: revoking is idempotent | DELETE /v1/stations/codes/j1 | 200 | 7.00 | 26 B | pass |
| List the workstations as an array of views | GET /v1/stations | 200 | 2.00 | 2 B | pass |
| Answer 404 for a workstation that does not exist | GET /v1/stations/w-t-missing | 404 | 0.00 | 31 B | pass |
| Answer 404 for a workstation id with a space in it | GET /v1/stations/no such | 404 | 1.00 | 31 B | pass |
| Answer 404 to a drain of a workstation that does not exist | POST /v1/stations/w-t-missing/drain | 404 | 1.00 | 31 B | pass |
| Answer 404 to an undrain of a workstation that does not exist | POST /v1/stations/w-t-missing/undrain | 404 | 1.00 | 31 B | pass |
| Answer 404 to a revoke of a workstation that does not exist | POST /v1/stations/w-t-missing/revoke | 404 | 2.00 | 31 B | pass |
| Answer 404 to a rename of a workstation that does not exist | PATCH /v1/stations/w-t-missing | 404 | 1.00 | 31 B | pass |
| Refuse a rename body with no name | PATCH /v1/stations/w-t-missing | 400 | 1.00 | 61 B | pass |
| Answer 404 for the settings of a workstation that does not exist | GET /v1/stations/w-t-missing/settings | 404 | 0.00 | 31 B | pass |
| Answer 404 to saving settings for a workstation that does not exist | PUT /v1/stations/w-t-missing/settings | 404 | 1.00 | 31 B | pass |
| Refuse workstation settings with a frame rate over 5 | PUT /v1/stations/w-t-missing/settings | 400 | 1.00 | 80 B | pass |
| Answer 404 for the leases of a workstation that does not exist | GET /v1/stations/w-t-missing/leases | 404 | 1.00 | 31 B | pass |
| Answer 404 to a lease on a workstation that does not exist | POST /v1/stations/w-t-missing/leases | 404 | 1.00 | 31 B | pass |
| Refuse a lease that names no agent | POST /v1/stations/w-t-missing/leases | 400 | 0.00 | 80 B | pass |
| Refuse a lease of a kind that is not shared or pixel | POST /v1/stations/w-t-missing/leases | 400 | 2.00 | 130 B | pass |
| Answer 404 to a release of a lease that does not exist | DELETE /v1/stations/w-t-missing/leases/l-t-missing | 404 | 1.00 | 25 B | pass |
| Answer 404 for the file sync state of a workstation that does not exist | GET /v1/stations/w-t-missing/sync | 404 | 0.00 | 31 B | pass |
| Answer 404 to resuming the sync of a workstation that does not exist | POST /v1/stations/w-t-missing/sync/resume | 404 | 1.00 | 31 B | pass |
| Refuse a sync resume whose body does not parse | POST /v1/stations/w-t-missing/sync/resume | 400 | 0.00 | 122 B | pass |
| Answer 404 for the mirrored files of a workstation that does not exist | GET /v1/stations/w-t-missing/files?prefix=notes | 404 | 0.00 | 31 B | pass |
| Answer 404 to reading a file of a workstation that does not exist | GET /v1/stations/w-t-missing/files/notes/t-stations.txt | 404 | 0.00 | 31 B | pass |
| Answer 404 to writing a file to a workstation that does not exist | PUT /v1/stations/w-t-missing/files/notes/t-stations.txt | 404 | 1.00 | 31 B | pass |
| Answer 404 to deleting a file of a workstation that does not exist | DELETE /v1/stations/w-t-missing/files/notes/t-stations.txt | 404 | 0.00 | 31 B | pass |
| Answer 404 to putting a file of a workstation that does not exist into the Library | POST /v1/stations/w-t-missing/files/notes/t-stations.txt | 404 | 0.00 | 74 B | pass |
| Read the recording defaults before changing them | GET /v1/stations/recording-defaults | 200 | 2.00 | 74 B | pass |
| Save new recording defaults for the workstations that join from now on | PUT /v1/stations/recording-defaults | 200 | 8.00 | 75 B | pass |
| Read back the saved recording defaults | GET /v1/stations/recording-defaults | 200 | 1.00 | 75 B | pass |
| Accept the longest retention the hub allows | PUT /v1/stations/recording-defaults | 200 | 20.0 | 76 B | pass |
| Refuse a retention beyond 3650 days | PUT /v1/stations/recording-defaults | 400 | 1.00 | 39 B | pass |
| Refuse a recording frame rate over 5 | PUT /v1/stations/recording-defaults | 400 | 1.00 | 80 B | pass |
| Refuse a recording frame rate that does not fit a byte | PUT /v1/stations/recording-defaults | 400 | 0.00 | 83 B | pass |
| Refuse recording defaults whose body does not parse | PUT /v1/stations/recording-defaults | 400 | 1.00 | 120 B | pass |
| Restore the recording defaults the hub had before this folder | PUT /v1/stations/recording-defaults | 200 | 35.0 | 74 B | pass |
| List the recordings of a workstation that has none | GET /v1/stations/recordings?station=w-t-missing | 200 | 2.00 | 2 B | pass |
| List recordings with a limit over the ceiling | GET /v1/stations/recordings?station=w-t-missing&limit=500 | 200 | 1.00 | 2 B | pass |
| Refuse a recordings list whose before is not a time | GET /v1/stations/recordings?before=yesterday | 400 | 1.00 | 44 B | pass |
| Answer 404 for a recording that does not exist | GET /v1/stations/recordings/r0-t-missing | 404 | 1.00 | 29 B | pass |
| Answer 404 for a frame of a recording that does not exist | GET /v1/stations/recordings/r0-t-missing/frames/1 | 404 | 1.00 | 25 B | pass |
| Refuse a frame number that is not a number | GET /v1/stations/recordings/r0-t-missing/frames/first | 400 | 1.00 | 53 B | pass |
| Answer 404 when deleting a recording that does not exist | DELETE /v1/stations/recordings/r0-t-missing | 404 | 1.00 | 29 B | pass |
| Answer 404 when a deleted recording is deleted again | DELETE /v1/stations/recordings/r0-t-missing | 404 | 1.00 | 29 B | pass |
| List the audit of a workstation with no rows | GET /v1/stations/audit?station=w-t-missing | 200 | 2.00 | 2 B | pass |
| List no audit rows at a limit of 0 | GET /v1/stations/audit?limit=0 | 200 | 1.00 | 2 B | pass |
| List the audit with a limit over the ceiling | GET /v1/stations/audit?limit=5000 | 200 | 2.00 | 905 B | pass |
| List the audit newest first | GET /v1/stations/audit?limit=50 | 200 | 1.00 | 905 B | pass |
| List the audit with a limit that is not a number | GET /v1/stations/audit?limit=lots | 200 | 2.00 | 905 B | pass |
| List the workstation VMs | GET /v1/stations/vms | 200 | 1.00 | 2 B | pass |
| Refuse a workstation VM on a node that does not exist | POST /v1/stations/vms | 404 | 1.00 | 23 B | pass |
| Refuse a workstation VM body that does not parse | POST /v1/stations/vms | 400 | 1.00 | 109 B | pass |
| Refuse a workstation VM body with no name | POST /v1/stations/vms | 400 | 1.00 | 61 B | pass |
| Refuse a workstation VM name with capital letters | POST /v1/stations/vms | 400 | 4.00 | 79 B | pass |
| Refuse a workstation VM name with non-ASCII letters | POST /v1/stations/vms | 400 | 1.00 | 75 B | pass |
| Refuse a workstation VM name of 64 characters | POST /v1/stations/vms | 400 | 1.00 | 133 B | pass |
| Refuse a workstation VM with no CPUs | POST /v1/stations/vms | 400 | 2.00 | 48 B | pass |
| Refuse a workstation VM with more CPUs than the hub allows | POST /v1/stations/vms | 400 | 1.00 | 49 B | pass |
| Pass the CPU ceiling and fail only at noded, which is absent | POST /v1/stations/vms | 502 | 62.0 | 101 B | pass |
| Refuse a workstation VM with less RAM than a Linux VM needs | POST /v1/stations/vms | 400 | 0.00 | 60 B | pass |
| Refuse a workstation VM with a disk under 8 GiB | POST /v1/stations/vms | 400 | 2.00 | 53 B | pass |
| Refuse a workstation VM with a GPU that is neither none, virtual nor vfio | POST /v1/stations/vms | 400 | 1.00 | 71 B | pass |
| Refuse a workstation VM on an OS it does not run | POST /v1/stations/vms | 400 | 1.00 | 63 B | pass |
| Refuse a Windows VM with no disk image | POST /v1/stations/vms | 400 | 1.00 | 90 B | pass |
| Refuse a Windows VM whose image is a relative path | POST /v1/stations/vms | 400 | 1.00 | 90 B | pass |
| Refuse a macOS VM on a machine that is not an Apple Mac | POST /v1/stations/vms | 400 | 1.00 | 83 B | pass |
| Refuse a workstation VM backend that is not auto, plaza or qemu | POST /v1/stations/vms | 400 | 1.00 | 63 B | pass |
| Refuse a valid Linux VM because noded cannot boot it | POST /v1/stations/vms | 502 | 14.0 | 101 B | pass |
| Answer 404 to sleeping a workstation VM that does not exist | POST /v1/stations/vms/wv-t-missing/sleep | 404 | 0.00 | 42 B | pass |
| Answer 404 to waking a workstation VM that does not exist | POST /v1/stations/vms/wv-t-missing/wake | 404 | 1.00 | 42 B | pass |
| Answer 404 when removing a workstation VM that does not exist | DELETE /v1/stations/vms/wv-t-missing | 404 | 4.00 | 42 B | pass |
| Answer 404 when a removed workstation VM is removed again | DELETE /v1/stations/vms/wv-t-missing | 404 | 1.00 | 42 B | pass |
| Refuse the workstation link without a bearer token | GET /v1/stations/link | 401 | 1.00 | 61 B | pass |
| Refuse the workstation link with a token no workstation holds | GET /v1/stations/link | 401 | 1.00 | 47 B | pass |
| Refuse the workstation link sent with a Basic scheme | GET /v1/stations/link | 401 | 1.00 | 61 B | pass |
| Refuse a live screen view that names no session | GET /v1/stations/w-t-missing/screen | 400 | 1.00 | 60 B | pass |
| Refuse a live screen view with more than 15 frames a second | GET /v1/stations/w-t-missing/screen?session=shared&fps=16 | 400 | 0.00 | 46 B | pass |
| Refuse a live screen view scaled below 0.1 | GET /v1/stations/w-t-missing/screen?session=shared&scale=0.05 | 400 | 1.00 | 43 B | pass |
| Answer 404 for the live screen of a workstation that does not exist | GET /v1/stations/w-t-missing/screen?session=shared | 404 | 1.00 | 31 B | pass |
| Answer 404 for the thumbnail of a workstation that does not exist | GET /v1/stations/w-t-missing/thumbnail?session=shared | 404 | 2.00 | 31 B | pass |
| Refuse a takeover socket that names no session | GET /v1/stations/w-t-missing/control | 400 | 1.00 | 60 B | pass |
| Answer 404 for a takeover of a workstation that does not exist | GET /v1/stations/w-t-missing/control?session=shared | 404 | 0.00 | 31 B | pass |
| Refuse an agent's lease on a workstation without the internal token | POST /internal/v1/agents/1/stations/w-t-missing/leases | 403 | 1.00 | 43 B | pass |
| Refuse an agent's lease with a wrong internal token | POST /internal/v1/agents/1/stations/w-t-missing/leases | 403 | 1.00 | 43 B | pass |
| Refuse an agent's lease whose agent id is not a number | POST /internal/v1/agents/abc/stations/w-t-missing/leases | 400 | 0.00 | 36 B | pass |
| Refuse an agent's lease whose body names another agent | POST /internal/v1/agents/1/stations/w-t-missing/leases | 400 | 1.00 | 62 B | pass |
| Refuse an agent's lease release without the internal token | DELETE /internal/v1/agents/1/stations/w-t-missing/leases/l-t-missing | 403 | 1.00 | 43 B | pass |
machines
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the machines on this plaza with the hub's own machine | GET /v1/machines | 200 | 1.00 | 58 B | pass |
| Refuse the local machine's facts when noded does not answer | GET /v1/machine | 502 | 3.00 | 101 B | pass |
| Read the fleet with the hub's machine and no noded | GET /v1/fleet | 200 | 1.00 | 295 B | pass |
volumes
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Refuse the volume list while no node answers | GET /v1/volumes | 502 | 1.00 | 101 B | pass |
| Refuse a volume create while no node answers | POST /v1/volumes | 502 | 0.00 | 101 B | pass |
| Refuse a volume delete while no node answers | DELETE /v1/volumes/t-vol-data | 502 | 0.00 | 101 B | pass |
| Refuse a volume delete for a name with a space | DELETE /v1/volumes/t vol | 502 | 1.00 | 101 B | pass |
| Refuse the warm template list while no node answers | GET /v1/templates | 502 | 1.00 | 101 B | pass |
| Refuse a warm template delete while no node answers | DELETE /v1/templates/t-warm-key | 502 | 1.00 | 101 B | pass |
society
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Register the first agent from its VM | POST /internal/v1/agents/43001/hello | 200 | 12.0 | 55.0 KiB | pass |
| Register the second agent from its VM | POST /internal/v1/agents/43002/hello | 200 | 4.00 | 55.0 KiB | pass |
| Delete agent 43002 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/43002 | 200 | 0.00 | 34 B | pass |
| Open a project so its tasks can be posted | POST /v1/projects | 201 | 42.0 | 674 B | pass |
| A task with no role makes no agora call | POST /v1/tasks | 201 | 1.00 | 285 B | pass |
| Refuse a roled task with no project | POST /v1/tasks | 400 | 1.00 | 93 B | pass |
| Refuse a roled task for a project that does not exist | POST /v1/tasks | 404 | 1.00 | 30 B | pass |
| Open a project for a roled task | POST /v1/projects | 201 | 12.0 | 664 B | pass |
| Post a task with a role | POST /v1/tasks | 201 | 3.00 | 295 B | pass |
| Read the open task | GET /v1/tasks/t9 | 200 | 0.00 | 295 B | pass |
| Open a project for a task that is withdrawn | POST /v1/projects | 201 | 65.0 | 688 B | pass |
| Post a task with a role to withdraw | POST /v1/tasks | 201 | 1.00 | 296 B | pass |
| Withdraw the open task | DELETE /v1/tasks/t10 | 200 | 1.00 | 309 B | pass |
| Refuse to withdraw a task twice | DELETE /v1/tasks/t10 | 409 | 0.00 | 57 B | pass |
| Read the withdrawn task | GET /v1/tasks/t10 | 200 | 1.00 | 309 B | pass |
| Delete agent 43001 again, so the hub has no live agent left for the nodes folder | DELETE /v1/agents/43001 | 200 | 1.00 | 34 B | pass |
runs
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the runs with the node unreachable | GET /v1/runs | 502 | 2.00 | 101 B | pass |
| Start a run with the node unreachable | POST /v1/runs | 502 | 1.00 | 101 B | pass |
| Read a run that was never started | GET /v1/runs/t-run-1 | 502 | 0.00 | 101 B | pass |
| Send input to a run that was never started | POST /v1/runs/t-run-1/input | 502 | 1.00 | 101 B | pass |
| Delete a run that was never started | DELETE /v1/runs/t-run-1 | 502 | 1.00 | 101 B | pass |
| Delete the same run again | DELETE /v1/runs/t-run-1 | 502 | 0.00 | 101 B | pass |
| Start a run with a body that is not JSON (the hub passes it on) | POST /v1/runs | 502 | 1.00 | 101 B | pass |
| Read a run with a non-numeric id | GET /v1/runs/abc | 502 | 0.00 | 101 B | pass |
| Send empty input to a run | POST /v1/runs/t-run-1/input | 502 | 0.00 | 101 B | pass |
| Read a run with id zero | GET /v1/runs/0 | 502 | 2.00 | 101 B | pass |
| Delete a run with a negative id | DELETE /v1/runs/-1 | 502 | 1.00 | 101 B | pass |
| Send unicode input to a run that was never started | POST /v1/runs/t-run-1/input | 502 | 1.00 | 101 B | pass |
| Start a run with an empty object | POST /v1/runs | 502 | 1.00 | 101 B | pass |
| Start a run with no body | POST /v1/runs | 502 | 1.00 | 101 B | pass |
| After the refused starts, the run list is still unreachable | GET /v1/runs | 502 | 1.00 | 101 B | pass |
loops
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the loops (none of ours yet) | GET /v1/loops | 200 | 1.00 | 2 B | pass |
| Refuse a loop for an agent that does not exist | POST /v1/loops | 400 | 1.00 | 27 B | pass |
| Refuse a loop body that is not JSON | POST /v1/loops | 400 | 1.00 | 68 B | pass |
| Refuse a loop with no max_runs | POST /v1/loops | 400 | 2.00 | 65 B | pass |
| Refuse a loop whose cid is a string | POST /v1/loops | 400 | 1.00 | 85 B | pass |
| Stop a loop that does not exist | DELETE /v1/loops/l999999 | 400 | 1.00 | 27 B | pass |
| Register an agent's VM without the internal token: refused | POST /internal/v1/agents/9101/hello | 403 | 1.00 | 43 B | pass |
| Register an agent's VM with a wrong internal token: refused | POST /internal/v1/agents/9101/hello | 403 | 1.00 | 43 B | pass |
| Register an agent with a cid that is not a number | POST /internal/v1/agents/abc/hello | 400 | 1.00 | 36 B | pass |
| Register an agent's VM with the internal token (the agent has no project) | POST /internal/v1/agents/9101/hello | 200 | 3.00 | 55.0 KiB | pass |
| A loop for an agent with no project is refused | POST /v1/loops | 400 | 0.00 | 81 B | pass |
| No loop for the agent with no project is listed | GET /v1/loops | 200 | 10.0 | 2 B | pass |
| Open a project for the loop's agent | POST /v1/projects | 201 | 22.0 | 682 B | pass |
| Start a loop for an agent with no project: refused | POST /v1/loops | 400 | 0.00 | 81 B | pass |
| No loop of an agent with no project is listed | GET /v1/loops | 200 | 0.00 | 2 B | pass |
| Refuse a loop with a blank prompt | POST /v1/loops | 400 | 1.00 | 56 B | pass |
| Refuse a loop with a prompt of 4001 characters | POST /v1/loops | 400 | 0.00 | 56 B | pass |
| Refuse a loop every zero minutes | POST /v1/loops | 400 | 1.00 | 66 B | pass |
| A loop with a unicode prompt is refused for an agent with no project | POST /v1/loops | 400 | 1.00 | 81 B | pass |
| A loop with a 4000-character prompt is refused for an agent with no project | POST /v1/loops | 400 | 1.00 | 81 B | pass |
| Stop a loop that was never started: refused | DELETE /v1/loops/never-started | 400 | 2.00 | 33 B | pass |
| Stop a loop that was never started: refused | DELETE /v1/loops/never-started | 400 | 1.00 | 33 B | pass |
| Stop a loop that was never started: refused | DELETE /v1/loops/never-started | 400 | 0.00 | 33 B | pass |
| Stop a loop that was never started: refused | DELETE /v1/loops/never-started | 400 | 1.00 | 33 B | pass |
| No loop of ours is listed | GET /v1/loops | 200 | 1.00 | 2 B | pass |
| Archive the test project | POST /v1/projects/pr24/archive | 200 | 3.00 | 757 B | pass |
| Mark the test agent's session gone | POST /internal/v1/agents/9101/gone | 200 | 1.00 | 2 B | pass |
usage
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Summarize the last 30 days with no window | GET /v1/usage/summary | 200 | 1.00 | 252 B | pass |
| Summarize an explicit window | GET /v1/usage/summary?from=0&to=3600000 | 200 | 1.00 | 234 B | pass |
| Refuse a summary whose from is not a number | GET /v1/usage/summary?from=soon | 400 | 2.00 | 53 B | pass |
| Refuse a summary whose to is not a number | GET /v1/usage/summary?to=soon | 400 | 1.00 | 51 B | pass |
| Show one hour's usage by model | GET /v1/usage/series?from=0&to=3600000&bucket=hour&by=model | 200 | 1.00 | 230 B | pass |
| Show one day's usage by agent | GET /v1/usage/series?from=0&to=3600000&bucket=day&by=agent | 200 | 0.00 | 231 B | pass |
| Show two hours of usage by role | GET /v1/usage/series?from=0&to=7200000&by=role | 200 | 0.00 | 392 B | pass |
| Show usage by provider | GET /v1/usage/series?from=0&to=3600000&by=provider | 200 | 1.00 | 233 B | pass |
| Refuse a series with an unknown bucket | GET /v1/usage/series?from=0&to=3600000&bucket=week | 400 | 1.00 | 52 B | pass |
| Refuse a series grouped by an unknown key | GET /v1/usage/series?from=0&to=3600000&by=team | 400 | 0.00 | 67 B | pass |
| Refuse a series whose window ends before it starts | GET /v1/usage/series?from=2000&to=1000 | 400 | 1.00 | 42 B | pass |
| Cap a year-long hourly series at 2000 points | GET /v1/usage/series?from=0&to=9007199254740991&bucket=hour | 200 | 3.00 | 336.0 KiB | pass |
| List the newest model calls with no filter | GET /v1/usage/calls | 200 | 3.00 | 41 B | pass |
| List calls at the limit of 200 | GET /v1/usage/calls?limit=200 | 200 | 1.00 | 41 B | pass |
| Refuse a calls page with a limit of zero | GET /v1/usage/calls?limit=0 | 400 | 0.00 | 44 B | pass |
| Refuse a calls page with a limit above 200 | GET /v1/usage/calls?limit=201 | 400 | 0.00 | 46 B | pass |
| Refuse a calls page whose limit is not a number | GET /v1/usage/calls?limit=many | 400 | 0.00 | 47 B | pass |
| Refuse a calls page whose before is not a number | GET /v1/usage/calls?before=x | 400 | 1.00 | 52 B | pass |
| Refuse a calls page whose cid is not a number | GET /v1/usage/calls?cid=x | 400 | 1.00 | 49 B | pass |
| Filter the calls by an encoded model id | GET /v1/usage/calls?model=anthropic%2Fclaude-opus-5-5&limit=5 | 200 | 1.00 | 41 B | pass |
| Filter the calls by agent | GET /v1/usage/calls?cid=1&limit=5 | 200 | 1.00 | 41 B | pass |
| List the usage providers before adding one | GET /v1/usage-providers | 200 | 3.00 | 2 B | pass |
| Answer 404 when reading a provider that does not exist | GET /v1/usage-providers/t-usage-hub/usage | 404 | 0.00 | 43 B | pass |
| Answer 404 when removing a provider that does not exist | DELETE /v1/usage-providers/t-usage-hub | 404 | 1.00 | 43 B | pass |
| Refuse a usage provider of another kind | POST /v1/usage-providers | 400 | 1.00 | 36 B | pass |
| Refuse a usage provider address with a query | POST /v1/usage-providers | 400 | 0.00 | 79 B | pass |
| Refuse a usage provider address that is not http | POST /v1/usage-providers | 400 | 1.00 | 51 B | pass |
| Refuse a usage provider address with credentials in it | POST /v1/usage-providers | 400 | 1.00 | 79 B | pass |
| Refuse a usage provider id with capitals and spaces | POST /v1/usage-providers | 400 | 1.00 | 42 B | pass |
| Refuse a usage provider with a blank key | POST /v1/usage-providers | 400 | 1.00 | 42 B | pass |
| Refuse a usage provider label longer than 60 characters | POST /v1/usage-providers | 400 | 1.00 | 44 B | pass |
| Refuse a usage provider body with an unknown field | POST /v1/usage-providers | 400 | 1.00 | 115 B | pass |
| Refuse a usage provider body without a key | POST /v1/usage-providers | 400 | 1.00 | 60 B | pass |
| Refuse a usage provider body that is not JSON | POST /v1/usage-providers | 400 | 1.00 | 106 B | pass |
| List the usage providers after the refused adds | GET /v1/usage-providers | 200 | 0.00 | 2 B | pass |
| List the subscription allowance windows | GET /v1/allowances | 200 | 0.00 | 2 B | pass |
toolbox
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Register a session for an agent the owner never spawned | POST /internal/v1/agents/9001/hello | 200 | 5.00 | 55.0 KiB | pass |
| Refuse an agent's hello without the internal token | POST /internal/v1/agents/9001/hello | 403 | 1.00 | 43 B | pass |
| Refuse an agent's hello with a wrong internal token | POST /internal/v1/agents/9001/hello | 403 | 1.00 | 43 B | pass |
| The owner's list is an array of discoveries with their fields | GET /v1/toolbox | 200 | 2.00 | 2 B | pass |
| Answer 404 when removing a discovery that does not exist | DELETE /v1/toolbox/d999999 | 404 | 1.00 | 32 B | pass |
| Answer 404 when removing an id that is not a discovery | DELETE /v1/toolbox/not-a-discovery | 404 | 1.00 | 40 B | pass |
| Answer 404 when flagging a discovery that does not exist | POST /v1/toolbox/d999999/flag | 404 | 1.00 | 32 B | pass |
| Answer 404 when clearing the flag of an unknown discovery | POST /v1/toolbox/d999999/flag | 404 | 1.00 | 32 B | pass |
| Refuse a flag whose body is not JSON, even for an unknown discovery | POST /v1/toolbox/d999999/flag | 400 | 1.00 | 101 B | pass |
| Refuse an agent message without the internal token | POST /internal/v1/agents/9001/message | 403 | 1.00 | 43 B | pass |
| Deny a tool request from an agent the hub does not know | POST /internal/v1/agents/9999/message | 200 | 1.00 | 111 B | pass |
| Refuse an agent message whose CID is not a number | POST /internal/v1/agents/abc/message | 400 | 1.00 | 36 B | pass |
| Refuse an agent message whose body is not JSON | POST /internal/v1/agents/9001/message | 400 | 2.00 | 111 B | pass |
| Record that the test agent's session ended | POST /internal/v1/agents/9001/gone | 200 | 1.00 | 2 B | pass |
connectors
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the connectors, with unsplash unset and no value anywhere | GET /v1/connectors | 200 | 1.00 | 26.8 KiB | pass |
| Answer 404 when updating the settings of an unknown connector | PUT /v1/connectors/t-no-such-connector | 404 | 1.00 | 46 B | pass |
| Answer 404 when setting a key for an unknown connector | PUT /v1/connectors/t-no-such-connector/secret | 404 | 1.00 | 46 B | pass |
| Answer 404 when testing an unknown connector | POST /v1/connectors/t-no-such-connector/test | 404 | 1.00 | 46 B | pass |
| Answer 404 when removing the key of an unknown connector | DELETE /v1/connectors/t-no-such-connector/secret | 404 | 1.00 | 46 B | pass |
| Refuse settings whose enabled flag is not a boolean | PUT /v1/connectors/unsplash | 400 | 1.00 | 90 B | pass |
| Refuse a settings body that is not JSON | PUT /v1/connectors/unsplash | 400 | 2.00 | 61 B | pass |
| Refuse an empty key value | PUT /v1/connectors/unsplash/secret | 400 | 1.00 | 26 B | pass |
| Enable unsplash for two roles and a project, trimming blank entries | PUT /v1/connectors/unsplash | 200 | 1.00 | 1.5 KiB | pass |
| Set unsplash's key, which the view then shows only as set | PUT /v1/connectors/unsplash/secret | 200 | 35.0 | 1.5 KiB | pass |
| Set one optional field, leaving the key as it is | PUT /v1/connectors/unsplash/secret | 200 | 62.0 | 1.5 KiB | pass |
| Refuse a key for a field unsplash does not have | PUT /v1/connectors/unsplash/secret | 400 | 0.00 | 53 B | pass |
| Refuse a key body that has no value | PUT /v1/connectors/unsplash/secret | 400 | 1.00 | 65 B | pass |
| List the connectors with unsplash configured and enabled | GET /v1/connectors | 200 | 1.00 | 26.8 KiB | pass |
| Refuse a key request with an empty body | PUT /v1/connectors/unsplash/secret | 400 | 1.00 | 65 B | pass |
| Remove one optional field's value, leaving the key | DELETE /v1/connectors/unsplash/secret?field=app_id | 200 | 20.0 | 1.5 KiB | pass |
| Remove every credential of unsplash, which is then unconfigured | DELETE /v1/connectors/unsplash/secret | 200 | 26.0 | 1.5 KiB | pass |
| Answer 404 when removing a key that is not stored | DELETE /v1/connectors/unsplash/secret | 404 | 1.00 | 50 B | pass |
| Test a connector with no key stored, which reports no key and calls nothing out | POST /v1/connectors/unsplash/test | 200 | 1.00 | 52 B | pass |
| Disable unsplash and clear its roles and projects again | PUT /v1/connectors/unsplash | 200 | 1.00 | 1.4 KiB | pass |
| List connector proposals, newest first, as a list | GET /v1/connectors/proposals | 200 | 1.00 | 2 B | pass |
| Answer 404 when reviewing a proposal that does not exist | POST /v1/connectors/proposals/cp-999/review | 404 | 1.00 | 32 B | pass |
| Refuse a review that does not say whether it approves | POST /v1/connectors/proposals/cp-999/review | 400 | 4.00 | 65 B | pass |
| Refuse a review body that is not JSON | POST /v1/connectors/proposals/cp-999/review | 400 | 1.00 | 55 B | pass |
| Refuse a manifest for a connector that does not exist | POST /v1/connectors/no-such-connector/manifest | 404 | 1.00 | 44 B | pass |
| Refuse a manifest that is not TOML | POST /v1/connectors/firecrawl/manifest | 400 | 1.00 | 157 B | pass |
reviews
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Answer 404 for a review run that does not exist | GET /v1/reviews/r-t-platforms-none | 404 | 1.00 | 26 B | pass |
| Answer 404 for the change tree of a review run that does not exist | GET /v1/reviews/r-t-platforms-none/tree | 404 | 1.00 | 26 B | pass |
| A task that does not exist has no review runs to list: 404 | GET /v1/tasks/t-platforms-unreviewed/reviews | 404 | 1.00 | 24 B | pass |
| A task with a unicode id that does not exist answers 404 | GET /v1/tasks/t-ünicode-✓/reviews | 404 | 0.00 | 24 B | pass |
| Answer 404 for a review id that only looks like a run | GET /v1/reviews/../r-1.2 | 404 | 3.00 | 26 B | pass |
| Open a project for the review tests | POST /v1/projects | 201 | 50.0 | 656 B | pass |
| Open a task for the review tests | POST /v1/tasks | 201 | 1.00 | 314 B | pass |
| A known task nothing has reviewed lists no runs and names the task | GET /v1/tasks/t11/reviews | 200 | 1.00 | 24 B | pass |
upgrades
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Upgrades of an unknown project | GET /v1/projects/no-such-project/upgrades | 404 | 1.00 | 27 B | pass |
| Fix of an advisory for an unknown project | POST /v1/projects/no-such-project/upgrades/fix | 404 | 1.00 | 27 B | pass |
| Run of the upgrades of an unknown project | POST /v1/projects/no-such-project/upgrades/run | 404 | 1.00 | 27 B | pass |
cves
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| CVE report of an unknown project | GET /v1/projects/no-such-project/cves | 404 | 1.00 | 27 B | pass |
| CVE export of an unknown project | GET /v1/projects/no-such-project/cves/export?format=csv | 404 | 0.00 | 27 B | pass |
| Poll of the advisory feeds for an unknown project | POST /v1/projects/no-such-project/cves/poll | 404 | 2.00 | 27 B | pass |
node-link
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Read this machine's link on a fresh hub: not joined, not running | GET /v1/node-link | 200 | 1.00 | 81 B | pass |
| Leave when not joined, which stops nothing and keeps the identity | POST /v1/node-link/leave | 200 | 1.00 | 81 B | pass |
| Leave and forget this node's identity when there is none | POST /v1/node-link/leave | 200 | 1.00 | 81 B | pass |
| Refuse a leave body that is not JSON | POST /v1/node-link/leave | 400 | 0.00 | 55 B | pass |
| Refuse a join with an empty code | POST /v1/node-link/join | 400 | 0.00 | 34 B | pass |
| Refuse a join code with a space in it | POST /v1/node-link/join | 400 | 1.00 | 67 B | pass |
| Refuse an option word passed as the join code | POST /v1/node-link/join | 400 | 1.00 | 35 B | pass |
| Refuse a join body with no code field | POST /v1/node-link/join | 400 | 1.00 | 34 B | pass |
| Refuse a join body that is not JSON | POST /v1/node-link/join | 400 | 0.00 | 66 B | pass |
| Refuse a well-formed join code the link cannot redeem, without echoing it | POST /v1/node-link/join | 400 | 103 | 67 B | pass |
| After the refused joins, this machine is still not joined | GET /v1/node-link | 200 | 1.00 | 81 B | pass |
ide
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the IDE's workspaces | GET /v1/ide/workspaces | 200 | 6.00 | 4.6 KiB | pass |
| The platform's own repo is not offered as a tree | GET /v1/ide/workspaces/repo:agentplaza@main/tree | 403 | 1.00 | 67 B | pass |
| The platform's own repo is not offered as a file | GET /v1/ide/workspaces/repo:agentplaza@main/file?path=README.md | 403 | 1.00 | 67 B | pass |
| The platform's second repo is not offered either | GET /v1/ide/workspaces/repo:agentplaza-mvp@main/tree | 403 | 1.00 | 67 B | pass |
| A repo that is not a project's repo is missing | GET /v1/ide/workspaces/repo:t-ide-nope@main/tree | 404 | 1.00 | 45 B | pass |
| A station that is not online is missing | GET /v1/ide/workspaces/station:w999/tree | 404 | 2.00 | 37 B | pass |
| An agent with no VM has no workspace | GET /v1/ide/workspaces/agent:999999/tree | 404 | 0.00 | 37 B | pass |
| Refuse a workspace id with no kind | GET /v1/ide/workspaces/nonsense/tree | 400 | 0.00 | 66 B | pass |
| Refuse an agent id with a leading zero | GET /v1/ide/workspaces/agent:007/tree | 400 | 1.00 | 68 B | pass |
| Refuse a repo id with no ref | GET /v1/ide/workspaces/repo:x/tree | 400 | 1.00 | 69 B | pass |
| Refuse a repo ref with a double dot | GET /v1/ide/workspaces/repo:x@a..b/tree | 400 | 1.00 | 69 B | pass |
| Refuse a station id without the w | GET /v1/ide/workspaces/station:12/tree | 400 | 1.00 | 83 B | pass |
| A write whose body is not JSON to a missing agent is missing | PUT /v1/ide/workspaces/agent:999999/file | 404 | 1.00 | 37 B | pass |
| A run whose body is not JSON to a missing agent is missing | POST /v1/ide/workspaces/agent:999999/runs | 404 | 1.00 | 37 B | pass |
| Refuse a checkpoint restore with a number that is not whole | POST /v1/ide/workspaces/agent:999999/checkpoints/abc/restore | 400 | 0.00 | 40 B | pass |
| A checkpoint restore of a missing workspace is missing | POST /v1/ide/workspaces/agent:999999/checkpoints/1/restore | 404 | 2.00 | 37 B | pass |
| Search a missing workspace | GET /v1/ide/workspaces/agent:999999/search?q=x | 404 | 1.00 | 37 B | pass |
| List the changes of a missing workspace | GET /v1/ide/workspaces/agent:999999/changes?base=HEAD | 404 | 1.00 | 37 B | pass |
| List the checkpoints of a missing workspace | GET /v1/ide/workspaces/agent:999999/checkpoints | 404 | 1.00 | 37 B | pass |
| Read a run of a missing workspace | GET /v1/ide/workspaces/agent:999999/runs/r1 | 404 | 0.00 | 37 B | pass |
| Stop a run of a missing workspace | POST /v1/ide/workspaces/agent:999999/runs/r1/stop | 404 | 1.00 | 37 B | pass |
| Open a pull request in a missing workspace | POST /v1/ide/workspaces/agent:999999/pulls | 404 | 0.00 | 37 B | pass |
| Create a branch in a missing workspace | POST /v1/ide/workspaces/agent:999999/branch | 404 | 1.00 | 37 B | pass |
| A pull request in the platform's repo is refused | POST /v1/ide/workspaces/repo:agentplaza@main/pulls | 403 | 1.00 | 67 B | pass |
| A branch in the platform's repo is refused | POST /v1/ide/workspaces/repo:agentplaza@main/branch | 403 | 0.00 | 67 B | pass |
| A pull request in a repo that is not a project's is missing | POST /v1/ide/workspaces/repo:t-ide-nope@main/pulls | 404 | 2.00 | 45 B | pass |
| A terminal socket on a missing agent is missing | GET /v1/ide/terminal/agent:999999 | 404 | 1.00 | 37 B | pass |
| A terminal socket on a repo is refused: a repo has no shell | GET /v1/ide/terminal/repo:agentplaza@main | 400 | 1.00 | 110 B | pass |
| A terminal socket on a malformed id is refused | GET /v1/ide/terminal/nonsense | 400 | 17.0 | 66 B | pass |
| A terminal socket on a missing station is missing | GET /v1/ide/terminal/station:w1 | 404 | 1.00 | 35 B | pass |
| A write to a missing station is missing | PUT /v1/ide/workspaces/station:w1/file | 404 | 1.00 | 35 B | pass |
| Hold the plaza with STOP ALL | POST /v1/plaza/stop | 200 | 1.00 | 79 B | pass |
| A station's tree is refused while the plaza is held | GET /v1/ide/workspaces/station:w1/tree | 409 | 0.00 | 80 B | pass |
| A station's search is refused while the plaza is held | GET /v1/ide/workspaces/station:w1/search?q=x | 409 | 1.00 | 80 B | pass |
| A station's write is refused while the plaza is held | PUT /v1/ide/workspaces/station:w1/file | 409 | 1.00 | 80 B | pass |
| The plaza state reads held | GET /v1/plaza/state | 200 | 0.00 | 79 B | pass |
| Resume the plaza | POST /v1/plaza/resume | 200 | 1.00 | 80 B | pass |
| A station that is not online is missing again once resumed | GET /v1/ide/workspaces/station:w1/tree | 404 | 1.00 | 35 B | pass |
| The plaza state reads resumed | GET /v1/plaza/state | 200 | 2.00 | 80 B | pass |
| List the IDE's audit log | GET /v1/ide/audit | 200 | 1.00 | 2 B | pass |
| The audit of a missing agent's workspace is empty | GET /v1/ide/audit?workspace=agent:999999 | 200 | 1.00 | 2 B | pass |
| The audit of a station is empty: refused writes are not audited | GET /v1/ide/audit?workspace=station:w1 | 200 | 1.00 | 2 B | pass |
| Refuse an audit limit of zero | GET /v1/ide/audit?limit=0 | 400 | 1.00 | 39 B | pass |
| Refuse an audit limit above a thousand | GET /v1/ide/audit?limit=1001 | 400 | 1.00 | 39 B | pass |
| Refuse an audit limit that is not a number | GET /v1/ide/audit?limit=abc | 400 | 0.00 | 44 B | pass |
| An audit limit of three returns at most three rows | GET /v1/ide/audit?limit=3 | 200 | 1.00 | 2 B | pass |
| Unfurl local references that do not exist | POST /v1/unfurl | 200 | 2.00 | 176 B | pass |
| Unfurl an empty batch | POST /v1/unfurl | 200 | 2.00 | 12 B | pass |
| Unfurl the largest batch allowed | POST /v1/unfurl | 200 | 2.00 | 1.6 KiB | pass |
| Refuse a batch over the limit | POST /v1/unfurl | 400 | 0.00 | 45 B | pass |
| Refuse a reference of an unknown kind | POST /v1/unfurl | 400 | 1.00 | 72 B | pass |
| Refuse a batch that is not JSON | POST /v1/unfurl | 400 | 1.00 | 72 B | pass |
| Refuse a batch with no refs | POST /v1/unfurl | 400 | 1.00 | 72 B | pass |
| Upload a file to unfurl | PUT /v1/files?name=t-ide-card.txt | 201 | 50.0 | 326 B | pass |
| Unfurl the file's library reference | POST /v1/unfurl | 200 | 1.00 | 255 B | pass |
| Unfurl a file URL on the hub | GET /v1/unfurl?url=/v1/files/fa64f6c7022e3 | 200 | 1.00 | 243 B | pass |
| Delete the file | DELETE /v1/files/fa64f6c7022e3 | 200 | 25.0 | 27 B | pass |
| The deleted file's reference is not found | POST /v1/unfurl | 200 | 0.00 | 44 B | pass |
| The deleted file's URL answers not found with status 200 | GET /v1/unfurl?url=/v1/files/fa64f6c7022e3 | 200 | 0.00 | 32 B | pass |
| Unfurl malformed remote references as unsupported without a call to GitHub | POST /v1/unfurl | 200 | 1.00 | 186 B | pass |
| Unfurl a GitHub pull URL whose number is not one as unsupported | GET /v1/unfurl?url=https://github.com/t-ide-nobody/t-ide-nope/pull/one | 200 | 1.00 | 34 B | pass |
| Unfurl a link the hub does not unfurl | GET /v1/unfurl?url=https://example.com/x | 200 | 1.00 | 34 B | pass |
| Refuse a single unfurl without a url | GET /v1/unfurl | 400 | 1.00 | 36 B | pass |
| Refuse a single unfurl of an empty url | GET /v1/unfurl?url= | 422 | 1.00 | 43 B | pass |
| Refuse a single unfurl of text that is no reference | GET /v1/unfurl?url=not%20a%20reference | 422 | 2.00 | 43 B | pass |
files
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Register agent 5 through its VM hello, so the agent routes take its uploads | POST /internal/v1/agents/5/hello | 200 | 5.00 | 55.0 KiB | pass |
| List the owner's attachments | GET /v1/files?kind=attachment | 200 | 1.00 | 2 B | pass |
| Refuse the metadata of a file that does not exist | GET /v1/files/fnope | 404 | 1.00 | 24 B | pass |
| Refuse the content of a file that does not exist | GET /v1/files/fnope/content | 404 | 1.00 | 24 B | pass |
| Refuse to delete a file that does not exist | DELETE /v1/files/fnope | 404 | 1.00 | 24 B | pass |
| Refuse an upload with no name | PUT /v1/files | 400 | 1.00 | 28 B | pass |
| Refuse an upload with an empty body | PUT /v1/files?name=t-files-empty.txt | 400 | 1.00 | 29 B | pass |
| Refuse an upload whose name is only dots | PUT /v1/files?name=.. | 400 | 1.00 | 29 B | pass |
| Refuse an upload whose name is too long | PUT /v1/files?name=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt | 400 | 2.00 | 50 B | pass |
| Refuse an agent's file without the internal token | PUT /internal/v1/agents/5/gateway/files?name=t-files-agent.txt | 403 | 1.00 | 43 B | pass |
| Refuse an agent's file with a wrong internal token | PUT /internal/v1/agents/5/gateway/files?name=t-files-agent.txt | 403 | 1.00 | 43 B | pass |
| Refuse an agent's file with no name | PUT /internal/v1/agents/5/gateway/files | 400 | 1.00 | 28 B | pass |
| Refuse an agent's file sent to a recipient that is not a number | PUT /internal/v1/agents/5/gateway/files?name=t-files-agent.txt&to=abc | 400 | 0.00 | 35 B | pass |
| Delete agent 5 again, so later folders do not see it | DELETE /v1/agents/5 | 200 | 1.00 | 34 B | pass |
| Upload a note as the owner | PUT /v1/files?name=t-files-notes.md&title=t-files%20notes | 201 | 51.0 | 331 B | pass |
| Read the note's metadata | GET /v1/files/fd91a74e580ba | 200 | 1.00 | 331 B | pass |
| Read the note's content | GET /v1/files/fd91a74e580ba/content | 200 | 1.00 | 36 B | pass |
| Download the note's content as an attachment | GET /v1/files/fd91a74e580ba/content?download=1 | 200 | 3.00 | 36 B | pass |
| The note is listed among the owner's attachments | GET /v1/files?kind=attachment | 200 | 1.00 | 333 B | pass |
| An octet-stream upload takes its type from the name | PUT /v1/files?name=t-files-data.csv | 201 | 90.0 | 328 B | pass |
| An upload without a title takes its name as the title | PUT /v1/files?name=t-files-untitled.txt | 201 | 47.0 | 338 B | pass |
| A name with accented letters and spaces is cleaned | PUT /v1/files?name=%C3%BCn%C3%AFcode%20notes.txt | 201 | 44.0 | 332 B | pass |
| A name with folders keeps only its last part | PUT /v1/files?name=..%2F..%2Ft-files-passwd | 201 | 33.0 | 326 B | pass |
| Upload a second version of the note under the same name | PUT /v1/files?name=t-files-notes.md | 201 | 25.0 | 334 B | pass |
| The old note is still there until it is deleted | GET /v1/files/fd91a74e580ba | 200 | 0.00 | 331 B | pass |
| Delete the old note | DELETE /v1/files/fd91a74e580ba | 200 | 13.0 | 27 B | pass |
| The old note's content is gone | GET /v1/files/fd91a74e580ba/content | 404 | 0.00 | 24 B | pass |
| The new note's content is the new version | GET /v1/files/f484c1c8ecba4/content | 200 | 0.00 | 38 B | pass |
| Only the new note has that name now | GET /v1/files?kind=attachment | 200 | 1.00 | 1.6 KiB | pass |
| Upload a picture as the owner | PUT /v1/files?name=t-files-pic.png | 201 | 77.0 | 328 B | pass |
| Share the picture with downloads off | POST /v1/shares | 201 | 51.0 | 457 B | pass |
| Read the picture's share data | GET /artifact/989d248261dc0f634bc8d00b04f90b20/data | 200 | 19.0 | 287 B | pass |
| A picture shows inline through its link even with downloads off | GET /artifact/989d248261dc0f634bc8d00b04f90b20/file | 200 | 12.0 | 14 B | pass |
| Share the note with downloads off | POST /v1/shares | 201 | 9.00 | 458 B | pass |
| A text file is refused through its link while downloads are off | GET /artifact/8f37cca22b69d13477a81ad1904f67d7/file | 403 | 2.00 | 44 B | pass |
| Refuse a version pin on a file link | POST /v1/shares | 400 | 1.00 | 45 B | pass |
| Turn downloads on for the note's link | PATCH /v1/shares/8f37cca22b69d13477a81ad1904f67d7 | 200 | 29.0 | 457 B | pass |
| Download the note through its link | GET /artifact/8f37cca22b69d13477a81ad1904f67d7/file | 200 | 20.0 | 38 B | pass |
| The note's link records the download | GET /v1/shares/8f37cca22b69d13477a81ad1904f67d7/events | 200 | 1.00 | 102 B | pass |
| Refuse rows through a file link: only artifacts have rows | GET /artifact/8f37cca22b69d13477a81ad1904f67d7/rows?panel=x | 404 | 1.00 | 19 B | pass |
| Revoke the note's link | DELETE /v1/shares/8f37cca22b69d13477a81ad1904f67d7 | 200 | 51.0 | 469 B | pass |
| Delete the picture | DELETE /v1/files/ff8dbc2c61268 | 200 | 32.0 | 27 B | pass |
| Read the deleted picture's metadata | GET /v1/files/ff8dbc2c61268 | 404 | 1.00 | 24 B | pass |
| Read the deleted picture's content | GET /v1/files/ff8dbc2c61268/content | 404 | 1.00 | 24 B | pass |
| Delete the picture a second time | DELETE /v1/files/ff8dbc2c61268 | 404 | 0.00 | 24 B | pass |
| The picture's link answers missing | GET /artifact/989d248261dc0f634bc8d00b04f90b20/data | 404 | 1.00 | 19 B | pass |
| Revoke the picture's link | DELETE /v1/shares/989d248261dc0f634bc8d00b04f90b20 | 200 | 28.0 | 469 B | pass |
| Delete the note | DELETE /v1/files/f484c1c8ecba4 | 200 | 22.0 | 27 B | pass |
| Delete the CSV upload | DELETE /v1/files/f32f7873b61ed | 200 | 23.0 | 27 B | pass |
| Delete the untitled upload | DELETE /v1/files/f3ccd5a1b2868 | 200 | 19.0 | 27 B | pass |
| Delete the accented-name upload | DELETE /v1/files/f9179c38c636e | 200 | 16.0 | 27 B | pass |
| Delete the folder-named upload | DELETE /v1/files/f136fb0ca037f | 200 | 40.0 | 27 B | pass |
| Register agent 5 through its VM hello, so the agent routes take its uploads | POST /internal/v1/agents/5/hello | 200 | 4.00 | 55.0 KiB | pass |
| An agent hands in a public file | PUT /internal/v1/agents/5/gateway/files?name=t-files-agent.txt&kind=deliverable | 201 | 59.0 | 122 B | pass |
| The owner lists the agent's file as made by the agent | GET /v1/files?kind=deliverable | 200 | 0.00 | 681 B | pass |
| The owner reads the agent's file | GET /v1/files/f6063bdce7a7f/content | 200 | 0.00 | 14 B | pass |
| The agent that made it downloads it | GET /internal/v1/agents/5/gateway/files/f6063bdce7a7f | 200 | 1.00 | 14 B | pass |
| Another agent downloads a public file | GET /internal/v1/agents/7/gateway/files/f6063bdce7a7f | 200 | 1.00 | 14 B | pass |
| An agent hands in a file private to agent 6 | PUT /internal/v1/agents/5/gateway/files?name=t-files-private.txt&to=6 | 201 | 56.0 | 122 B | pass |
| The agent it is shared with reads it | GET /internal/v1/agents/6/gateway/files/f1204f5ceee02 | 200 | 1.00 | 7 B | pass |
| An agent it is not shared with cannot read it | GET /internal/v1/agents/7/gateway/files/f1204f5ceee02 | 404 | 1.00 | 24 B | pass |
| The owner reads the private file | GET /v1/files/f1204f5ceee02/content | 200 | 0.00 | 7 B | pass |
| Delete the public agent file | DELETE /v1/files/f6063bdce7a7f | 200 | 46.0 | 27 B | pass |
| Delete the private agent file | DELETE /v1/files/f1204f5ceee02 | 200 | 20.0 | 27 B | pass |
| Delete agent 5 again, so later folders do not see it | DELETE /v1/agents/5 | 200 | 0.00 | 34 B | pass |
console
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| The start page answers missing while no console is built | GET / | 404 | 1.00 | 21 B | pass |
| The index file answers missing too | GET /index.html | 404 | 1.00 | 21 B | pass |
| A console asset answers missing | GET /assets/main.js | 404 | 0.00 | 21 B | pass |
| A console asset path that climbs out is missing | GET /assets/../secret | 404 | 0.00 | 21 B | pass |
| A console file that does not exist is missing | GET /favicon.ico | 404 | 0.00 | 21 B | pass |
| A console file path that climbs out is missing | GET /../secret | 404 | 1.00 | 21 B | pass |
| An empty asset name is missing | GET /assets/ | 404 | 0.00 | 28 B | pass |
| The start page is missing for a browser that asks for brotli | GET / | 404 | 1.00 | 21 B | pass |
egress
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Store the credential the rule will add | POST /v1/vault/secrets | 200 | 56.0 | 330 B | pass |
| List the rules before any is made | GET /v1/egress/inject | 200 | 0.00 | 12 B | pass |
| Create a rule that adds the credential to one host's GET requests | POST /v1/egress/inject | 200 | 33.0 | 235 B | pass |
| List the rule with its settings and no value | GET /v1/egress/inject | 200 | 1.00 | 247 B | pass |
| Change the rule's methods, path and state | PATCH /v1/egress/inject/egr-d8ac21ab0ab4 | 200 | 34.0 | 249 B | pass |
| Refuse a host written as a URL | PATCH /v1/egress/inject/egr-d8ac21ab0ab4 | 400 | 1.00 | 93 B | pass |
| Refuse a secret that is not bound to the egress consumer | PATCH /v1/egress/inject/egr-d8ac21ab0ab4 | 400 | 1.00 | 84 B | pass |
| Refuse a change to an unknown rule | PATCH /v1/egress/inject/egress_rule_nope | 404 | 1.00 | 45 B | pass |
| Enable the rule again | PATCH /v1/egress/inject/egr-d8ac21ab0ab4 | 200 | 16.0 | 248 B | pass |
| Refuse a rule on plain HTTP | POST /v1/egress/inject | 400 | 1.00 | 70 B | pass |
| Refuse a host written with a scheme | POST /v1/egress/inject | 400 | 1.00 | 93 B | pass |
| Refuse a format without the secret's placeholder | POST /v1/egress/inject | 400 | 1.00 | 45 B | pass |
| Refuse a header the proxy sets itself | POST /v1/egress/inject | 400 | 1.00 | 73 B | pass |
| Refuse a header name with a space | POST /v1/egress/inject | 400 | 1.00 | 54 B | pass |
| Refuse a path prefix that does not start with a slash | POST /v1/egress/inject | 400 | 1.00 | 59 B | pass |
| Refuse a method that is not a token | POST /v1/egress/inject | 400 | 1.00 | 43 B | pass |
| Refuse a port of zero | POST /v1/egress/inject | 400 | 1.00 | 34 B | pass |
| Refuse a secret that is not in the vault | POST /v1/egress/inject | 400 | 1.00 | 84 B | pass |
| Refuse a rule that names no secret | POST /v1/egress/inject | 400 | 1.00 | 39 B | pass |
| Refuse a rule body that is not JSON | POST /v1/egress/inject | 400 | 1.00 | 56 B | pass |
| Refuse a change to a rule that does not exist | PATCH /v1/egress/inject/egress_rule_nope | 404 | 1.00 | 45 B | pass |
| Refuse a delete of a rule that does not exist | DELETE /v1/egress/inject/egress_rule_nope | 404 | 2.00 | 45 B | pass |
| Refuse the egress rules from a host that does not name loopback | GET /v1/egress/inject | 403 | 1.00 | 46 B | pass |
| Delete the rule | DELETE /v1/egress/inject/egr-d8ac21ab0ab4 | 200 | 16.0 | 11 B | pass |
| The deleted rule is no longer listed | GET /v1/egress/inject | 200 | 1.00 | 12 B | pass |
| Refuse a second delete of the same rule | DELETE /v1/egress/inject/egr-d8ac21ab0ab4 | 404 | 1.00 | 45 B | pass |
| Delete the credential the rule used | DELETE /v1/vault/secrets/sec_sknvwd2icknl | 204 | 41.0 | 0 B | pass |
acp
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Read the settings to restore later | GET /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| List the editor sessions | GET /v1/acp/sessions | 200 | 1.00 | 2 B | pass |
| Refuse an editor socket while the editors are off | GET /v1/acp | 409 | 1.00 | 104 B | pass |
| Refuse an editor socket from a host that does not name loopback | GET /v1/acp | 403 | 0.00 | 46 B | pass |
| Turn the editors on, keeping the other settings | PUT /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| Refuse an editor socket from a foreign origin | GET /v1/acp | 403 | 1.00 | 57 B | pass |
| Refuse an editor socket with the wrong WebSocket version | GET /v1/acp | 426 | 1.00 | 44 B | pass |
| Refuse an editor request that is not a WebSocket upgrade | GET /v1/acp | 400 | 1.00 | 79 B | pass |
| Refuse a WebSocket key that is not 16 bytes | GET /v1/acp | 400 | 0.00 | 55 B | pass |
| Answer the ACP agent registry, or 502 when it cannot be fetched | GET /v1/acp/registry | 200 | 247 | 15.4 KiB | pass |
| Answer the ACP agent registry again with a fresh fetch | GET /v1/acp/registry?refresh=1 | 200 | 276 | 15.4 KiB | pass |
| Turn the editors off again, restoring the saved settings | PUT /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| Refuse an editor socket once the editors are off | GET /v1/acp | 409 | 0.00 | 104 B | pass |
nodes
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the plaza's machines | GET /v1/nodes | 200 | 1.00 | 681 B | pass |
| List the join codes: no token or secret is ever listed | GET /v1/nodes/codes | 200 | 1.00 | 121 B | pass |
| Answer 404 for the machine of a node the hub does not know | GET /v1/nodes/n999/machine | 404 | 1.00 | 24 B | pass |
| Answer 404 for the live stats of a node the hub does not know | GET /v1/nodes/n999/stats | 404 | 1.00 | 24 B | pass |
| Answer 404 for the runs of a node the hub does not know | GET /v1/nodes/n999/runs | 404 | 3.00 | 24 B | pass |
| Issue a join code for a new node | POST /v1/nodes/codes | 201 | 1.00 | 221 B | pass |
| Issue a join code whose lifetime is clamped to one hour | POST /v1/nodes/codes | 201 | 0.00 | 221 B | pass |
| Refuse a join code whose ttl is not a number | POST /v1/nodes/codes | 400 | 0.00 | 85 B | pass |
| Refuse a join code with a negative ttl | POST /v1/nodes/codes | 400 | 0.00 | 83 B | pass |
| List the join codes: the new ones are active and unused | GET /v1/nodes/codes | 200 | 0.00 | 359 B | pass |
| Issue a join code whose label is longer than 64 characters | POST /v1/nodes/codes | 201 | 1.00 | 221 B | pass |
| Join a new node with a code: 201 and its CID range | POST /v1/nodes/join | 201 | 1.00 | 67 B | pass |
| Refuse to join with a code that was already used | POST /v1/nodes/join | 403 | 0.00 | 39 B | pass |
| The used code names the node it made | GET /v1/nodes/codes | 200 | 0.00 | 530 B | pass |
| The joined node is listed, active and online | GET /v1/nodes | 200 | 1.00 | 1.3 KiB | pass |
| Refuse to revoke a code that was already used (404) | DELETE /v1/nodes/codes/c2 | 404 | 1.00 | 38 B | pass |
| Issue a second join code to be revoked | POST /v1/nodes/codes | 201 | 0.00 | 221 B | pass |
| Revoke an unused join code | DELETE /v1/nodes/codes/c5 | 200 | 1.00 | 26 B | pass |
| Revoking a revoked code again is still an answer 200 | DELETE /v1/nodes/codes/c5 | 200 | 0.00 | 26 B | pass |
| Refuse a join with a revoked code | POST /v1/nodes/join | 403 | 2.00 | 39 B | pass |
| The revoked code is listed as revoked | GET /v1/nodes/codes | 200 | 1.00 | 648 B | pass |
| Revoke a joined node's access | POST /v1/nodes/n1/revoke | 200 | 0.00 | 695 B | pass |
| Revoking a revoked node again is still an answer 200 | POST /v1/nodes/n1/revoke | 200 | 0.00 | 695 B | pass |
| The revoked node is listed as revoked and offline | GET /v1/nodes | 200 | 1.00 | 1.3 KiB | pass |
| Refuse to stop a revoked node (409) | POST /v1/nodes/n1/stop | 409 | 0.00 | 30 B | pass |
| Refuse to resume a revoked node (409) | POST /v1/nodes/n1/resume | 409 | 1.00 | 30 B | pass |
| Refuse to drain on a revoked node (409) | POST /v1/nodes/n1/drain | 409 | 1.00 | 30 B | pass |
| Answer 409 for a revoked node's machine: refused at once | GET /v1/nodes/n1/machine | 409 | 1.00 | 30 B | pass |
| Answer 409 for a revoked node's live stats | GET /v1/nodes/n1/stats | 409 | 1.00 | 30 B | pass |
| Answer 409 for a revoked node's runs | GET /v1/nodes/n1/runs | 409 | 1.00 | 30 B | pass |
| Refuse a run on a revoked node (409) | POST /v1/nodes/n1/runs | 409 | 1.00 | 30 B | pass |
| Refuse an unsigned heartbeat from a node | POST /v1/nodes/n1/heartbeat | 401 | 0.00 | 25 B | pass |
| Refuse an unsigned command poll from a node | GET /v1/nodes/n1/commands | 401 | 0.00 | 25 B | pass |
| Refuse an unsigned command result from a node | POST /v1/nodes/n1/results | 401 | 1.00 | 25 B | pass |
| Refuse an unsigned metrics batch from a node | POST /v1/nodes/n1/metrics | 401 | 0.00 | 25 B | pass |
| Refuse an unsigned heartbeat from a node that does not exist | POST /v1/nodes/n999/heartbeat | 401 | 1.00 | 25 B | pass |
| Answer 404 when revoking an unknown node | POST /v1/nodes/n999/revoke | 404 | 2.00 | 24 B | pass |
| Answer 404 when revoking the hub's own machine (it is not a joined node) | POST /v1/nodes/n0/revoke | 404 | 1.00 | 24 B | pass |
| Answer 404 when stopping an unknown node | POST /v1/nodes/n999/stop | 404 | 0.00 | 24 B | pass |
| Refuse an unsigned tunnel open from a node | GET /v1/nodes/n1/tunnel | 401 | 0.00 | 25 B | pass |
| Answer 502 for the hub's own machine while its noded is down | GET /v1/nodes/n0/machine | 502 | 1.00 | 101 B | pass |
| Answer 502 for the hub's live stats while its noded is down | GET /v1/nodes/n0/stats | 502 | 1.00 | 101 B | pass |
| Answer 502 for the hub's runs while its noded is down | GET /v1/nodes/n0/runs | 502 | 1.00 | 101 B | pass |
| Answer 502 for a run on the hub's machine while its noded is down | POST /v1/nodes/n0/runs | 502 | 1.00 | 101 B | pass |
| Refuse a stop whose now flag is not a boolean | POST /v1/nodes/n0/stop | 400 | 1.00 | 90 B | pass |
| Drain on the hub's own machine: it goes out of service | POST /v1/nodes/n0/drain | 200 | 30.0 | 679 B | pass |
| Refuse a drain whose on flag is not a boolean | POST /v1/nodes/n0/drain | 400 | 1.00 | 90 B | pass |
| Drain off the hub's own machine: it is back in service | POST /v1/nodes/n0/drain | 200 | 41.0 | 679 B | pass |
| Answer 404 when draining an unknown node | POST /v1/nodes/n999/drain | 404 | 0.00 | 24 B | pass |
| Refuse a rebalance whose dry_run flag is not a boolean | POST /v1/nodes/rebalance | 400 | 1.00 | 92 B | pass |
| Rebalance now: the answer lists the moves and whether they started | POST /v1/nodes/rebalance | 200 | 1.00 | 28 B | pass |
| Refuse a path change for a node the hub does not know | POST /v1/nodes/n999/path | 404 | 1.00 | 24 B | pass |
| Refuse a mesh path without a host and port | POST /v1/nodes/n999/path | 400 | 1.00 | 72 B | pass |
| Refuse a node's link query without its signature | GET /v1/nodes/n1/link | 401 | 1.00 | 25 B | pass |
engines
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| read the settings and keep them | GET /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| switch the background health checks off | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| List the coding engines: the built-in ones | GET /v1/engines | 200 | 0.00 | 6.2 KiB | pass |
| Show one built-in engine | GET /v1/engines/claude | 200 | 1.00 | 465 B | pass |
| Answer 404 for an unknown engine | GET /v1/engines/no-such-engine | 404 | 1.00 | 38 B | pass |
| Refuse a built-in engine's accent that is not #rrggbb | PUT /v1/engines/claude | 400 | 2.00 | 43 B | pass |
| Refuse an engine variable the plaza sets for its gateway | PUT /v1/engines/claude | 400 | 1.00 | 76 B | pass |
| A refused edit that also turns the engine off changes nothing | PUT /v1/engines/claude | 400 | 1.00 | 43 B | pass |
| Pin a built-in engine back to the catalogue's version (no network) | PUT /v1/engines/claude | 200 | 1.00 | 362 B | pass |
| Refuse a version with characters an npm version never has | PUT /v1/engines/claude | 400 | 1.00 | 87 B | pass |
| Refuse to remove a built-in engine | DELETE /v1/engines/claude | 400 | 1.00 | 56 B | pass |
| Answer 404 when editing an unknown engine | PUT /v1/engines/no-such-engine | 404 | 0.00 | 38 B | pass |
| Refuse an engine without an id | POST /v1/engines | 400 | 0.00 | 26 B | pass |
| Refuse an engine id with capitals and a space | POST /v1/engines | 400 | 1.00 | 42 B | pass |
| Refuse an engine that is neither built-in, registry nor local | POST /v1/engines | 400 | 1.00 | 76 B | pass |
| Refuse an engine without its engine kind | POST /v1/engines | 400 | 1.00 | 30 B | pass |
| Refuse a local ACP engine with no command | POST /v1/engines | 400 | 1.00 | 97 B | pass |
| Refuse a command that holds its arguments | POST /v1/engines | 400 | 1.00 | 97 B | pass |
| Refuse an engine variable the plaza sets for its gateway | POST /v1/engines | 400 | 1.00 | 90 B | pass |
| Refuse a key placed in an engine's environment | POST /v1/engines | 400 | 1.00 | 180 B | pass |
| Refuse a version for a local command engine (only npm engines have one) | POST /v1/engines | 400 | 0.00 | 66 B | pass |
| Refuse more arguments than an engine may take | POST /v1/engines | 400 | 1.00 | 58 B | pass |
| Refuse an instance whose id is a built-in engine's (409) | POST /v1/engines | 409 | 0.00 | 34 B | pass |
| Refuse a registry instance whose id is taken (409, before any registry fetch) | POST /v1/engines | 409 | 1.00 | 34 B | pass |
| Add an engine instance as a local ACP command | POST /v1/engines | 201 | 1.00 | 425 B | pass |
| Refuse an instance whose id exists (409) | POST /v1/engines | 409 | 0.00 | 39 B | pass |
| The refused instances were not kept | GET /v1/engines/t-eng-bad | 404 | 1.00 | 33 B | pass |
| Read the local engine instance | GET /v1/engines/t-eng-local | 200 | 1.00 | 425 B | pass |
| Change a local engine's label, accent, arguments, variables and route | PUT /v1/engines/t-eng-local | 200 | 1.00 | 462 B | pass |
| Refuse a bad accent edit (400, nothing changes) | PUT /v1/engines/t-eng-local | 400 | 1.00 | 43 B | pass |
| Refuse a route without a model (400, nothing changes) | PUT /v1/engines/t-eng-local | 400 | 1.00 | 46 B | pass |
| Refuse a bad home folder (400, nothing changes) | PUT /v1/engines/t-eng-local | 400 | 1.00 | 51 B | pass |
| Refuse a lower case variable name (400, nothing changes) | PUT /v1/engines/t-eng-local | 400 | 0.00 | 58 B | pass |
| Refuse a key in the environment on edit (400, nothing changes) | PUT /v1/engines/t-eng-local | 400 | 0.00 | 178 B | pass |
| Refuse a command with arguments in it on edit (400, nothing changes) | PUT /v1/engines/t-eng-local | 400 | 1.00 | 97 B | pass |
| Refuse changing the id (400, nothing changes) | PUT /v1/engines/t-eng-local | 400 | 1.00 | 51 B | pass |
| Refuse an unknown field (400, nothing changes) | PUT /v1/engines/t-eng-local | 400 | 2.00 | 178 B | pass |
| Refuse a version on a local command (400, nothing changes) | PUT /v1/engines/t-eng-local | 400 | 0.00 | 66 B | pass |
| Turn a local engine off: it shows off | PUT /v1/engines/t-eng-local | 200 | 1.00 | 452 B | pass |
| Turn a local engine back on | PUT /v1/engines/t-eng-local | 200 | 1.00 | 462 B | pass |
| Change a local engine's command | PUT /v1/engines/t-eng-local | 200 | 1.00 | 464 B | pass |
| Answer 404 when editing an unknown instance | PUT /v1/engines/no-such-engine | 404 | 1.00 | 38 B | pass |
| The engine listing shows the instance as changed | GET /v1/engines | 200 | 0.00 | 6.5 KiB | pass |
| Enable Mistral Vibe, a pip engine: the check reads it without the registry | PUT /v1/engines/vibe | 200 | 6.00 | 337 B | pass |
| Turn Claude Code off so a check does not fetch from npm | PUT /v1/engines/claude | 200 | 1.00 | 362 B | pass |
| Turn Codex off | PUT /v1/engines/codex | 200 | 0.00 | 342 B | pass |
| Turn OpenCode off | PUT /v1/engines/opencode | 200 | 1.00 | 340 B | pass |
| Check every enabled engine: the off ones answer off | POST /v1/engines/check | 200 | 1.00 | 1.0 KiB | pass |
| Refuse an update whose dry_run is not a boolean (the update always asks npm, so only validation is tested) | POST /v1/engines/update | 400 | 0.00 | 92 B | pass |
| Refuse an update body that is not JSON | POST /v1/engines/update | 400 | 0.00 | 112 B | pass |
| Restore Claude Code's switch as it was | PUT /v1/engines/claude | 200 | 1.00 | 465 B | pass |
| Restore Codex's switch as it was | PUT /v1/engines/codex | 200 | 2.00 | 445 B | pass |
| Restore OpenCode's switch as it was | PUT /v1/engines/opencode | 200 | 1.00 | 443 B | pass |
| Turn Mistral Vibe off again | PUT /v1/engines/vibe | 200 | 1.00 | 327 B | pass |
| Remove an engine instance the owner added | DELETE /v1/engines/t-eng-local | 200 | 0.00 | 25 B | pass |
| Answer 404 for a removed engine instance | GET /v1/engines/t-eng-local | 404 | 1.00 | 35 B | pass |
| Answer 404 when removing an instance twice | DELETE /v1/engines/t-eng-local | 404 | 1.00 | 35 B | pass |
| Answer 404 when editing a removed engine instance | PUT /v1/engines/t-eng-local | 404 | 0.00 | 35 B | pass |
| The engine listing no longer has the removed instance | GET /v1/engines | 200 | 0.00 | 6.2 KiB | pass |
| the settings go back to what they were before the folder | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
auth
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Check that the session is signed in | GET /v1/auth/check | 200 | 1.00 | 11 B | pass |
| Refuse a sign-in body with no token | POST /v1/auth/session | 400 | 0.00 | 37 B | pass |
| Refuse a sign-in body that is not JSON | POST /v1/auth/session | 400 | 0.00 | 37 B | pass |
| Refuse an empty token | POST /v1/auth/session | 401 | 0.00 | 39 B | pass |
| Rotate the owner token: the new one is answered once | POST /v1/hub/owner-token/rotate | 200 | 51.0 | 60 B | pass |
| Sign in with the rotated owner token: a session cookie is set | POST /v1/auth/session | 200 | 1.00 | 11 B | pass |
| Sign out of this browser: the cookie is cleared | DELETE /v1/auth/session | 200 | 1.00 | 11 B | pass |
| Signing out twice is still an answer 200 | DELETE /v1/auth/session | 200 | 2.00 | 11 B | pass |
tasks
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Open a project for the task tests | POST /v1/projects | 201 | 13.0 | 676 B | pass |
| Open a task with a goal, an acceptance line, a role and a budget | POST /v1/tasks | 201 | 0.00 | 333 B | pass |
| Read the open task back | GET /v1/tasks/t12 | 200 | 1.00 | 333 B | pass |
| Open a second task that runs after the first | POST /v1/tasks | 201 | 0.00 | 315 B | pass |
| List the project's tasks: both are there | GET /v1/tasks?project=pr26 | 200 | 0.00 | 661 B | pass |
| List only the tasks that can be claimed now: the first, not the second | GET /v1/tasks?ready=true | 200 | 1.00 | 2.2 KiB | pass |
| Relink the second task to run after nothing: it is ready | PATCH /v1/tasks/t13 | 200 | 0.00 | 304 B | pass |
| Relink the second task after the first again: it waits | PATCH /v1/tasks/t13 | 200 | 1.00 | 315 B | pass |
| Refuse a relink that would close a cycle | PATCH /v1/tasks/t12 | 400 | 1.00 | 83 B | pass |
| Refuse a relink to a task that does not exist | PATCH /v1/tasks/t12 | 400 | 1.00 | 52 B | pass |
| Refuse a relink whose after is not a list | PATCH /v1/tasks/t12 | 400 | 1.00 | 104 B | pass |
| Answer 404 when relinking a task that does not exist | PATCH /v1/tasks/t999999 | 404 | 1.00 | 35 B | pass |
| Refuse a claim by an agent that is not connected | POST /v1/tasks/t12/claim | 404 | 0.00 | 40 B | pass |
| Refuse a claim with no agent named | POST /v1/tasks/t12/claim | 400 | 2.00 | 79 B | pass |
| Answer 404 when claiming a task that does not exist | POST /v1/tasks/t999999/claim | 404 | 0.00 | 35 B | pass |
| Refuse finishing a task that was never claimed | POST /v1/tasks/t12/finish | 409 | 0.00 | 47 B | pass |
| Refuse a finish with an outcome that is neither done nor failed | POST /v1/tasks/t12/finish | 400 | 0.00 | 108 B | pass |
| Answer 404 when finishing a task that does not exist | POST /v1/tasks/t999999/finish | 404 | 1.00 | 35 B | pass |
| Refuse moving a task that already belongs to a project | POST /v1/tasks/t12/move | 409 | 0.00 | 72 B | pass |
| Refuse a move with no project named | POST /v1/tasks/t12/move | 400 | 1.00 | 46 B | pass |
| Refuse a move into a project that does not exist | POST /v1/tasks/t12/move | 404 | 1.00 | 31 B | pass |
| Answer 404 when moving a task that does not exist | POST /v1/tasks/t999999/move | 404 | 2.00 | 35 B | pass |
| A task's review runs: none yet | GET /v1/tasks/t12/reviews | 200 | 1.00 | 24 B | pass |
| Review runs of an unknown task are not a server error | GET /v1/tasks/t999999/reviews | 404 | 1.00 | 24 B | pass |
| The project's graph: A first, B after it, A is the frontier | GET /v1/projects/pr26/graph | 200 | 0.00 | 507 B | pass |
| Refuse a task body that is not JSON | POST /v1/tasks | 400 | 1.00 | 109 B | pass |
| Refuse a task with no goal | POST /v1/tasks | 400 | 0.00 | 76 B | pass |
| Refuse a task with a goal that is only spaces | POST /v1/tasks | 400 | 1.00 | 34 B | pass |
| Refuse a task whose goal is longer than 4096 bytes | POST /v1/tasks | 400 | 1.00 | 43 B | pass |
| Refuse a task outside any project | POST /v1/tasks | 400 | 1.00 | 93 B | pass |
| Refuse a task for a project that does not exist | POST /v1/tasks | 404 | 1.00 | 31 B | pass |
| Refuse a task that runs after a task that does not exist | POST /v1/tasks | 400 | 0.00 | 52 B | pass |
| Refuse a task that runs after the same task twice | POST /v1/tasks | 400 | 1.00 | 33 B | pass |
| Refuse a task assigned to an agent that is not connected | POST /v1/tasks | 404 | 1.00 | 40 B | pass |
| Open a task with a unicode goal | POST /v1/tasks | 201 | 0.00 | 319 B | pass |
| Withdraw the unicode task | DELETE /v1/tasks/t14 | 200 | 0.00 | 332 B | pass |
| Answer 404 for a task that does not exist | GET /v1/tasks/t999999 | 404 | 1.00 | 35 B | pass |
| List the tasks of a project that does not exist: none | GET /v1/tasks?project=pr999999 | 200 | 1.00 | 12 B | pass |
| Withdraw an open task that has links: A is withdrawn | DELETE /v1/tasks/t12 | 200 | 1.00 | 346 B | pass |
| Withdrawing a withdrawn task is refused | DELETE /v1/tasks/t12 | 409 | 0.00 | 57 B | pass |
| A withdrawn task cannot be relinked | PATCH /v1/tasks/t12 | 409 | 0.00 | 71 B | pass |
| The task after the withdrawn one is blocked by it: not ready, blocked_by A | GET /v1/tasks/t13 | 200 | 0.00 | 315 B | pass |
| Withdraw the blocked task | DELETE /v1/tasks/t13 | 200 | 0.00 | 322 B | pass |
| Archive the project: the rest goes back to the plaza | POST /v1/projects/pr26/archive | 200 | 2.00 | 1020 B | pass |
| Read the spec of a task that does not exist: 404 | GET /v1/tasks/t999999/spec | 404 | 0.00 | 36 B | pass |
| Open a project whose tasks will be blocked | POST /v1/projects | 201 | 8.00 | 684 B | pass |
| Open the upstream task | POST /v1/tasks | 201 | 1.00 | 292 B | pass |
| Open a task that runs after the upstream one | POST /v1/tasks | 201 | 1.00 | 305 B | pass |
| Withdraw the upstream task | DELETE /v1/tasks/t15 | 200 | 1.00 | 305 B | pass |
| The task after a withdrawn one is blocked: not ready, blocked by it | GET /v1/tasks/t16 | 200 | 1.00 | 305 B | pass |
| The graph keeps the blocked task out of the frontier | GET /v1/projects/pr27/graph | 200 | 1.00 | 450 B | pass |
| Withdrawing the upstream task again is refused | DELETE /v1/tasks/t15 | 409 | 1.00 | 57 B | pass |
| Relink the blocked task to run after nothing: it is ready again | PATCH /v1/tasks/t16 | 200 | 0.00 | 294 B | pass |
| Withdraw the now-ready task | DELETE /v1/tasks/t16 | 200 | 1.00 | 307 B | pass |
| Archive the project: the budget goes back to the plaza | POST /v1/projects/pr27/archive | 200 | 1.00 | 938 B | pass |
work-engine
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Open a project for the work engine's requests | POST /v1/projects | 201 | 11.0 | 687 B | pass |
| List the sessions | GET /v1/sessions | 200 | 1.00 | 135.1 KiB | pass |
| List the live sessions of the project | GET /v1/sessions?project=pr28&live=true | 200 | 0.00 | 2 B | pass |
| Refuse a session state the hub does not know | GET /v1/sessions?state=sleeping | 400 | 0.00 | 42 B | pass |
| Answer 404 to a session that does not exist | GET /v1/sessions/s999999 | 404 | 1.00 | 30 B | pass |
| Answer 404 to the transcript of a session that does not exist | GET /v1/sessions/s999999/transcript?after=0&limit=10 | 404 | 1.00 | 30 B | pass |
| Answer 404 to the context of a session that does not exist | GET /v1/sessions/s999999/context | 404 | 0.00 | 30 B | pass |
| Answer 404 to stopping a session that does not exist | POST /v1/sessions/s999999/stop | 404 | 1.00 | 30 B | pass |
| List the seats of a task that does not exist: none | GET /v1/tasks/t999999/seats | 200 | 0.00 | 2 B | pass |
| List the threads of a task that does not exist: none | GET /v1/tasks/t999999/threads | 200 | 1.00 | 2 B | pass |
| Answer 404 to a thread that does not exist | GET /v1/threads/th999999 | 404 | 1.00 | 30 B | pass |
| Answer a thread that does not exist: 404 | POST /v1/threads/th999999/answer | 404 | 0.00 | 48 B | pass |
| Read the project's budget with its disk | GET /v1/projects/pr28/budget | 200 | 1.00 | 103 B | pass |
| Set the project's budget with a disk quota | PUT /v1/projects/pr28/budget | 200 | 62.0 | 310 B | pass |
| Read the project's budget with its quota and what its disk holds | GET /v1/projects/pr28/budget | 200 | 1.00 | 370 B | pass |
| Read the project budget's ledger, newest first | GET /v1/projects/pr28/budget/ledger?limit=5 | 200 | 1.00 | 143 B | pass |
| Refuse a disk quota of zero bytes | PUT /v1/projects/pr28/budget | 400 | 0.00 | 68 B | pass |
| Refuse a project budget field the hub does not know | PUT /v1/projects/pr28/budget | 400 | 2.00 | 146 B | pass |
| Clear the disk quota: null means no quota | PUT /v1/projects/pr28/budget | 200 | 25.0 | 304 B | pass |
| Answer 404 to a budget set on a project that does not exist | PUT /v1/projects/pr999999/budget | 404 | 0.00 | 31 B | pass |
| Refuse a project budget with no unit | PUT /v1/projects/pr28/budget | 400 | 1.00 | 64 B | pass |
| Read the budget of a task that does not exist: no cap | GET /v1/tasks/t999999/budget | 200 | 1.00 | 52 B | pass |
| Answer 404 to a budget set on a task that does not exist | PUT /v1/tasks/t999999/budget | 404 | 1.00 | 27 B | pass |
| Read the ledger of a task that does not exist: none | GET /v1/tasks/t999999/budget/ledger | 200 | 1.00 | 2 B | pass |
| Read the project budget's ledger with a limit of one entry | GET /v1/projects/pr28/budget/ledger?limit=1 | 200 | 0.00 | 143 B | pass |
| List the cards, answered ones too | GET /v1/cards | 200 | 2.00 | 14.6 KiB | pass |
| List the open cards: every one is open | GET /v1/cards?open=true | 200 | 0.00 | 12.3 KiB | pass |
| List the open cards of the main chat | GET /v1/cards?open=1&chat=main | 200 | 0.00 | 1.1 KiB | pass |
| Answer 404 to a card that does not exist | GET /v1/cards/c999999 | 404 | 1.00 | 27 B | pass |
| Answer 404 to answering a card that does not exist | POST /v1/cards/c999999 | 404 | 0.00 | 27 B | pass |
| Refuse an answer that names no choice | POST /v1/cards/c999999 | 400 | 1.00 | 66 B | pass |
| List the chats: the main chat is there | GET /v1/chats | 200 | 1.00 | 7.0 KiB | pass |
| Open a chat with the project's planner | POST /v1/chats | 201 | 3.00 | 210 B | pass |
| Answer 404 to a chat for a project that does not exist | POST /v1/chats | 404 | 0.00 | 31 B | pass |
| Refuse a chat field the hub does not know | POST /v1/chats | 400 | 0.00 | 95 B | pass |
| Read the project chat with its messages | GET /v1/chats/ch25 | 200 | 0.00 | 210 B | pass |
| Read the main chat with the last five messages | GET /v1/chats/main?limit=5 | 200 | 0.00 | 1.4 KiB | pass |
| Answer 404 to a chat that does not exist | GET /v1/chats/ch999999 | 404 | 0.00 | 28 B | pass |
| List the messages of the project chat after the start | GET /v1/chats/ch25/messages?after=0&limit=10 | 200 | 0.00 | 2 B | pass |
| Answer 404 to the messages of a chat that does not exist | GET /v1/chats/ch999999/messages | 404 | 1.00 | 28 B | pass |
| Say something in the project chat | POST /v1/chats/ch25/messages | 201 | 28.0 | 128 B | pass |
| Refuse a message of spaces only | POST /v1/chats/ch25/messages | 400 | 1.00 | 25 B | pass |
| Answer 404 to a message in a chat that does not exist | POST /v1/chats/ch999999/messages | 404 | 1.00 | 28 B | pass |
| Rename the project chat | PATCH /v1/chats/ch25 | 200 | 33.0 | 201 B | pass |
| Archive the project chat: it leaves the list | PATCH /v1/chats/ch25 | 200 | 10.0 | 200 B | pass |
| List the archived chats: the project chat is among them | GET /v1/chats?archived=true | 200 | 1.00 | 7.2 KiB | pass |
| Restore the project chat | PATCH /v1/chats/ch25 | 200 | 12.0 | 201 B | pass |
| Refuse a chat edit field the hub does not know | PATCH /v1/chats/ch25 | 400 | 1.00 | 109 B | pass |
| Answer 404 to renaming a chat that does not exist | PATCH /v1/chats/ch999999 | 404 | 1.00 | 28 B | pass |
| Read a page of the main chat's transcript before a seq | GET /v1/chats/main/transcript?before=1000000&limit=500 | 200 | 1.00 | 27 B | pass |
| Refuse a transcript page before what is not a seq | GET /v1/chats/main/transcript?before=last&limit=10 | 400 | 1.00 | 41 B | pass |
previews
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List a project's previews: none for a project with no lanes | GET /v1/projects/p999/previews | 200 | 1.00 | 15 B | pass |
| Answer 404 when sharing a preview the hub does not know | POST /v1/previews/pv999/links | 404 | 1.00 | 28 B | pass |
| Answer 404 for the links of a preview the hub does not know | GET /v1/previews/pv999/links | 404 | 1.00 | 28 B | pass |
| Answer 404 when revoking a link the hub does not know | DELETE /v1/preview-links/pl999 | 404 | 12.0 | 25 B | pass |
| Answer 404 for the openings of a link the hub does not know | GET /v1/preview-links/pl999/openings | 404 | 0.00 | 25 B | pass |
| Answer 404 for the QR code of a link the hub does not know | GET /v1/preview-links/pl999/qr | 404 | 0.00 | 25 B | pass |
| Answer 404 when sleeping a preview the hub does not know | POST /v1/previews/pv999/sleep | 404 | 1.00 | 28 B | pass |
| Answer 404 when waking a preview the hub does not know | POST /v1/previews/pv999/wake | 404 | 1.00 | 28 B | pass |
| Answer 404 when closing a preview the hub does not know | POST /v1/previews/pv999/close | 404 | 0.00 | 28 B | pass |
intake
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| The intake view lists the three sources, none set, and no items | GET /v1/intake | 200 | 1.00 | 429 B | pass |
| Refuse a source the plaza does not have | PUT /v1/intake/connections/jira | 404 | 1.00 | 33 B | pass |
| Refuse a Linear base address over plain http to another host | PUT /v1/intake/connections/linear | 400 | 1.00 | 90 B | pass |
| A GitHub delivery to a token that is not the connector's is a 404, and never kept | POST /v1/hooks/github/no-such-token | 404 | 1.00 | 27 B | pass |
flows
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| the plaza starts released | GET /v1/plaza/state | 200 | 1.00 | 80 B | pass |
| open a project to hold | POST /v1/projects | 201 | 8.00 | 694 B | pass |
| post a task before the hold | POST /v1/tasks | 201 | 1.00 | 302 B | pass |
| read the settings before the hold | GET /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| stop all with a reason | POST /v1/plaza/stop | 200 | 1.00 | 89 B | pass |
| the state reads as held | GET /v1/plaza/state | 200 | 2.00 | 89 B | pass |
| the project reads still answer while held | GET /v1/projects/pr29 | 200 | 1.00 | 783 B | pass |
| the task reads still answer while held | GET /v1/tasks/t17 | 200 | 0.00 | 302 B | pass |
| a task is still accepted while held and waits | POST /v1/tasks | 201 | 0.00 | 302 B | pass |
| a project is still opened while held | POST /v1/projects | 201 | 2.00 | 570 B | pass |
| a settings save is kept while held | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| a model call through the gateway is refused while held | POST /internal/v1/agents/1/gateway/anthropic/v1/messages | 409 | 1.00 | 73 B | pass |
| a decision is refused while held | POST /v1/decisions | 409 | 2.00 | 73 B | pass |
| an E2B key can still be made while held | POST /v1/e2b/keys | 200 | 12.0 | 130 B | pass |
| a sandbox start is refused as held | POST /e2b/sandboxes | 409 | 1.00 | 118 B | pass |
| the project goes to maintenance while held | POST /v1/projects/pr29/mode | 200 | 1.00 | 969 B | pass |
| a maintenance sweep is refused as held | POST /v1/projects/pr29/maintenance/sweep | 409 | 1.00 | 43 B | pass |
| a malformed stop changes nothing | POST /v1/plaza/stop | 400 | 1.00 | 93 B | pass |
| the state is still held with its reason | GET /v1/plaza/state | 200 | 1.00 | 89 B | pass |
| a repeated stop keeps the hold | POST /v1/plaza/stop | 200 | 2.00 | 79 B | pass |
| resume the plaza | POST /v1/plaza/resume | 200 | 1.00 | 80 B | pass |
| resume again is harmless | POST /v1/plaza/resume | 200 | 1.00 | 80 B | pass |
| the model call gets past the hold | POST /internal/v1/agents/1/gateway/anthropic/v1/messages | 404 | 0.00 | 25 B | pass |
| the decision fails on its config, not the hold | POST /v1/decisions | 400 | 0.00 | 106 B | pass |
| the sandbox fails on its node, not the hold | POST /e2b/sandboxes | 503 | 1.00 | 148 B | pass |
| the sweep fails for want of a watcher, not the hold | POST /v1/projects/pr29/maintenance/sweep | 409 | 1.00 | 102 B | pass |
| a task posted during the hold is still open | GET /v1/tasks/t18 | 200 | 1.00 | 302 B | pass |
| the held project still lists both tasks | GET /v1/projects/pr29 | 200 | 1.00 | 969 B | pass |
| the held E2B key is removed | DELETE /v1/e2b/keys/e2k-bf2bd1209d0a | 200 | 11.0 | 11 B | pass |
| archive the held project | POST /v1/projects/pr29/archive | 200 | 0.00 | 1.0 KiB | pass |
| archive the second held project | POST /v1/projects/pr30/archive | 200 | 0.00 | 645 B | pass |
| the ended project takes no new task | POST /v1/tasks | 409 | 0.00 | 59 B | pass |
| open a project with no budget of its own | POST /v1/projects | 201 | 9.00 | 698 B | pass |
| a new project has no budget of its own | GET /v1/projects/pr31/budget | 200 | 1.00 | 103 B | pass |
| a task draws on the project's budget | POST /v1/tasks | 201 | 1.00 | 296 B | pass |
| a second task draws on the budget | POST /v1/tasks | 201 | 1.00 | 296 B | pass |
| pause the project | POST /v1/projects/pr31/pause | 200 | 0.00 | 950 B | pass |
| pausing again changes nothing | POST /v1/projects/pr31/pause | 200 | 0.00 | 950 B | pass |
| move the project to maintenance while paused | POST /v1/projects/pr31/mode | 200 | 1.00 | 1.0 KiB | pass |
| a maintenance sweep is refused while paused | POST /v1/projects/pr31/maintenance/sweep | 409 | 0.00 | 46 B | pass |
| a task posted while paused waits unclaimed | POST /v1/tasks | 201 | 1.00 | 305 B | pass |
| resume the project | POST /v1/projects/pr31/resume | 200 | 1.00 | 1.2 KiB | pass |
| resume again is harmless | POST /v1/projects/pr31/resume | 200 | 1.00 | 1.2 KiB | pass |
| the project stays in maintenance | GET /v1/projects/pr31 | 200 | 1.00 | 1.2 KiB | pass |
| a signal goes into the inbox | POST /v1/projects/pr31/maintenance/signals | 201 | 0.00 | 122 B | pass |
| make an ingest address for the project | POST /v1/projects/pr31/maintenance/ingest | 201 | 1.00 | 139 B | pass |
| a signal from outside arrives at the address | POST /v1/ingest/apzi_cfb3e31eccade63dc00f4456510e8c076452b511ac3f768f | 202 | 1.00 | 11 B | pass |
| the maintenance view counts both signals | GET /v1/projects/pr31/maintenance | 200 | 1.00 | 596 B | pass |
| a sweep with no one to watch the project is refused | POST /v1/projects/pr31/maintenance/sweep | 409 | 0.00 | 102 B | pass |
| the owner's signal can be deleted | DELETE /v1/projects/pr31/maintenance/signals/s1 | 200 | 0.00 | 16 B | pass |
| deleting that signal again is not found | DELETE /v1/projects/pr31/maintenance/signals/s1 | 404 | 1.00 | 26 B | pass |
| stop the ingest address | DELETE /v1/projects/pr31/maintenance/ingest | 200 | 1.00 | 16 B | pass |
| the stopped address refuses a signal | POST /v1/ingest/apzi_cfb3e31eccade63dc00f4456510e8c076452b511ac3f768f | 404 | 1.00 | 34 B | pass |
| archive the project from maintenance | POST /v1/projects/pr31/archive | 200 | 1.00 | 1.3 KiB | pass |
| the first open task is withdrawn | GET /v1/tasks/t19 | 200 | 0.00 | 309 B | pass |
| the second open task is withdrawn | GET /v1/tasks/t20 | 200 | 1.00 | 309 B | pass |
| the paused-time task is withdrawn | GET /v1/tasks/t21 | 200 | 0.00 | 318 B | pass |
| a paused project cannot be paused now | POST /v1/projects/pr31/pause | 409 | 2.00 | 36 B | pass |
| an archived project takes no new task | POST /v1/tasks | 409 | 1.00 | 59 B | pass |
| an archived project cannot go back to build | POST /v1/projects/pr31/mode | 409 | 1.00 | 36 B | pass |
| archiving again is harmless | POST /v1/projects/pr31/archive | 200 | 1.00 | 1.3 KiB | pass |
| the archived project still reads | GET /v1/projects/pr31 | 200 | 1.00 | 1.3 KiB | pass |
| open a project for the active branch | POST /v1/projects | 201 | 14.0 | 704 B | pass |
| a task draws 500 from it | POST /v1/tasks | 201 | 1.00 | 293 B | pass |
| archive straight from active | POST /v1/projects/pr32/archive | 200 | 2.00 | 868 B | pass |
| the active task is withdrawn | GET /v1/tasks/t22 | 200 | 0.00 | 306 B | pass |
| the hub enforces the model policy and keeps a snapshot | GET /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| an orchestrator model off its tier is refused | PUT /v1/settings | 400 | 1.00 | 174 B | pass |
| an orchestrator role model off its tier is refused | PUT /v1/settings | 400 | 0.00 | 174 B | pass |
| a worker role model may be any model | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| a model added to the orchestrator list can be used | PUT /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| a model in use cannot be dropped from the orchestrator list | PUT /v1/settings | 400 | 1.00 | 174 B | pass |
| the orchestrator goes back to the default model and the policy with it | PUT /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| favourite models are kept in order | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| a duplicate favourite is refused | PUT /v1/settings | 400 | 0.00 | 71 B | pass |
| a favourite without a provider is refused | PUT /v1/settings | 400 | 1.00 | 73 B | pass |
| a favourite with a space is refused | PUT /v1/settings | 400 | 1.00 | 88 B | pass |
| the list holds exactly 200 favourites | PUT /v1/settings | 200 | 1.00 | 4.4 KiB | pass |
| a 201st favourite is refused | PUT /v1/settings | 400 | 0.00 | 53 B | pass |
| the favourites are cleared | PUT /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| zero turns the health checks off | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| a day is the most the interval may be | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| an interval over a day is refused | PUT /v1/settings | 400 | 1.00 | 78 B | pass |
| a negative interval is refused as a bad value | PUT /v1/settings | 400 | 1.00 | 94 B | pass |
| the default interval comes back | PUT /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| zero turns is refused | PUT /v1/settings | 400 | 1.00 | 44 B | pass |
| 200 turns is the most | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| 201 turns is refused | PUT /v1/settings | 400 | 1.00 | 46 B | pass |
| the saved settings go back to the snapshot | PUT /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| a read after the restore matches the snapshot | GET /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| read the settings and keep a snapshot of the interval | GET /v1/settings | 200 | 0.00 | 1.2 KiB | pass |
| switch the background provider checks off | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
| the vault is unsealed | GET /v1/vault | 200 | 1.00 | 254 B | pass |
| the provider has no key yet | GET /v1/providers/anthropic | 200 | 1.00 | 429 B | pass |
| a test refuses without a key, offline | POST /v1/providers/anthropic/test | 200 | 29.0 | 171 B | pass |
| Note where the audit log ends before the key is added, so the audit check can page from there | GET /v1/vault/audit?verify=1&limit=1 | 200 | 4.00 | 296 B | pass |
| add the key through the vault | POST /v1/vault/secrets | 200 | 47.0 | 353 B | pass |
| a second plaza entry under the same name is refused | POST /v1/vault/secrets | 409 | 1.00 | 83 B | pass |
| the provider reads a key now | GET /v1/providers/anthropic | 200 | 0.00 | 428 B | pass |
| the consumer resolves to the new entry | GET /v1/vault/consumers | 200 | 1.00 | 5.8 KiB | pass |
| the provider list agrees | GET /v1/providers | 200 | 1.00 | 7.0 KiB | pass |
| the audit records the key being created | GET /v1/vault/audit?verify=1&after=208&limit=500 | 200 | 3.00 | 332 B | pass |
| the vault list shows the entry and no value | GET /v1/vault/secrets | 200 | 2.00 | 2.5 KiB | pass |
| Note where the audit log ends before the key is removed, so the audit check can page from there | GET /v1/vault/audit?verify=1&limit=1 | 200 | 3.00 | 296 B | pass |
| remove the key | DELETE /v1/vault/secrets/sec_isxahkgd4ih3 | 204 | 24.0 | 0 B | pass |
| the provider reads no key again | GET /v1/providers/anthropic | 200 | 0.00 | 429 B | pass |
| the consumer is unresolved again | GET /v1/vault/consumers | 200 | 1.00 | 5.8 KiB | pass |
| a test refuses again, offline | POST /v1/providers/anthropic/test | 200 | 28.0 | 171 B | pass |
| a repeated delete is not found | DELETE /v1/vault/secrets/sec_isxahkgd4ih3 | 404 | 1.00 | 26 B | pass |
| the audit records the removal | GET /v1/vault/audit?verify=1&after=209&limit=500 | 200 | 4.00 | 674 B | pass |
| the settings go back to the snapshot | PUT /v1/settings | 200 | 1.00 | 1.2 KiB | pass |
recipes
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| List the recipe engine's recipes | GET /v1/recipes | 200 | 5.00 | 2.1 KiB | pass |
| Show the change recipe as its TOML | GET /v1/recipes/change | 200 | 1.00 | 1.6 KiB | pass |
| A recipe version that is not newer is refused | PUT /v1/recipes/change | 400 | 1.00 | 67 B | pass |
| List recipe runs | GET /v1/recipe-runs | 200 | 2.00 | 25.6 KiB | pass |
| An unknown recipe run is not found | GET /v1/recipe-runs/rr-0/graph | 404 | 0.00 | 30 B | pass |
| Cancelling an unknown recipe run is not found | POST /v1/recipe-runs/rr-0/cancel | 404 | 2.00 | 30 B | pass |
| Queueing CI checks for an unknown project is not found | POST /v1/recipe-runs/ci | 404 | 0.00 | 27 B | pass |
workflows
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| The workflows of an unknown project are not found | GET /v1/projects/no-such-project/workflows | 404 | 0.00 | 27 B | pass |
| A workflow of an unknown project is not found | GET /v1/projects/no-such-project/workflows/ship | 404 | 1.00 | 43 B | pass |
| A manual run of an unknown project's workflow is not found | POST /v1/projects/no-such-project/workflows/ship/runs | 404 | 1.00 | 27 B | pass |
| The GitHub Actions import preview of an unknown project is not found | GET /v1/projects/no-such-project/workflows/import | 404 | 1.00 | 27 B | pass |
| Saving an import of an unknown project is not found | POST /v1/projects/no-such-project/workflows/import | 404 | 0.00 | 27 B | pass |
| The runs of a change of an unknown project are not found | GET /v1/projects/no-such-project/changes/ch-1/workflow-runs | 404 | 1.00 | 27 B | pass |
releases
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| The releases of an unknown project are not found | GET /v1/projects/no-such-project/releases | 404 | 1.00 | 27 B | pass |
| An unknown release is not found | GET /v1/releases/rel-0 | 404 | 1.00 | 27 B | pass |
| A candidate of an unknown release is not found | POST /v1/releases/rel-0/candidate | 404 | 1.00 | 27 B | pass |
| The report of an unknown release is not found | GET /v1/releases/rel-0/report | 404 | 1.00 | 27 B | pass |
| A yank without a reason is refused | POST /v1/releases/rel-0/yank | 400 | 1.00 | 27 B | pass |
| A lane of an unknown release is not found | POST /v1/releases/rel-0/lane | 404 | 1.00 | 27 B | pass |
| A backport to an unknown project is not found | POST /v1/projects/no-such-project/backports | 404 | 1.00 | 27 B | pass |
| The release channels are listed | GET /v1/release-channels | 200 | 0.00 | 273 B | pass |
| A sync of an unknown release is not found | POST /v1/releases/rel-0/sync | 404 | 0.00 | 27 B | pass |
| Edited notes of an unknown release are not found | POST /v1/releases/rel-0/notes | 404 | 1.00 | 27 B | pass |
| Notes without text are refused | POST /v1/releases/rel-0/notes | 400 | 2.00 | 34 B | pass |
| A compatibility matrix of an unknown release is not found | POST /v1/releases/rel-0/compatibility | 404 | 1.00 | 27 B | pass |
| A compatibility matrix that does not parse is refused | POST /v1/releases/rel-0/compatibility | 400 | 1.00 | 187 B | pass |
triggers
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| Adding a cron schedule to a recipe | POST /v1/recipes/intake/triggers | 201 | 1.00 | 238 B | pass |
| A cron expression with a field out of range is refused | POST /v1/recipes/intake/triggers | 400 | 1.00 | 38 B | pass |
| List a recipe's triggers | GET /v1/recipes/intake/triggers | 200 | 1.00 | 253 B | pass |
| A webhook with a token that is not the connector's is not found | POST /v1/hooks/github/no-such-token | 404 | 1.00 | 27 B | pass |
| The owner's hand on an unknown recipe is not found | POST /v1/recipes/no-such-recipe/fire | 404 | 1.00 | 26 B | pass |
| Removing an unknown trigger is not found | DELETE /v1/recipes/intake/triggers/rt-0 | 404 | 2.00 | 27 B | pass |
qualification
| Request | Call | Status | ms | Size | Result |
|---|---|---|---|---|---|
| An unknown release has no qualification | GET /v1/releases/rel-0/qualification | 404 | 0.00 | 27 B | pass |
| A qualification of an unknown release is not found | POST /v1/releases/rel-0/qualify | 404 | 0.00 | 27 B | pass |
| The report of an unknown release is not found | GET /v1/releases/rel-0/report | 404 | 0.00 | 27 B | pass |
| The tests of an unknown project are not found | GET /v1/projects/pr-0/tests | 404 | 1.00 | 27 B | pass |
| The trends of an unknown project are not found | GET /v1/projects/pr-0/trends | 404 | 1.00 | 27 B | pass |
| Lab tests of an unknown run are not found | POST /v1/test-lab/runs/q0/tests | 404 | 0.00 | 23 B | pass |
| Ending an unknown lab run is not found | POST /v1/test-lab/runs/q0/end | 404 | 1.00 | 23 B | pass |
| A test fixed without a reason is refused | POST /v1/projects/pr-0/tests/cart::pay/fixed | 400 | 1.00 | 43 B | pass |
Timings
Every GET in the spec without path parameters, requested 20 times in a row on the CI runner, slowest p95 first. Times are milliseconds on a shared runner: compare them run to run, not to production. The last column is the p95's change against main.
| Route | What it does | Status | p50 | p95 | max | p95 | Size | vs main |
|---|---|---|---|---|---|---|---|---|
| /v1/overview | Read the hub's overview | 200 | 0.98 | 32.9 | 32.9 | 225 B | -39% | |
| /v1/metrics/export | Export a view as CSV | 200 | 7.60 | 10.7 | 10.7 | 24 B | -30% | |
| /v1/foundry/models | List the foundry's models | 200 | 0.96 | 7.24 | 7.24 | 3.2 KiB | +234% | |
| /v1/artifacts | List the artifacts, newest first (?project=, ?q=) | 200 | 0.51 | 7.21 | 7.21 | 1.9 KiB | -31% | |
| /v1/metrics/view | Compose a metrics view | 200 | 5.01 | 7.02 | 7.02 | 3.6 KiB | -56% | |
| /v1/prices | List the model prices | 200 | 4.14 | 6.99 | 6.99 | 520.2 KiB | -48% | |
| /v1/recipes | List the review recipes, the recipe engine's recipes and the orchestrator's proposals | 200 | 4.07 | 6.01 | 6.01 | 2.1 KiB | -45% | |
| /v1/ide/workspaces | List the IDE's workspaces | 200 | 5.06 | 6.00 | 6.00 | 5.8 KiB | -72% | |
| /v1/evals/archive | List the archived tasks with a known outcome, the benchmark's cases | 200 | 0.99 | 5.15 | 5.15 | 199 B | -31% | |
| /v1/files | List the files agents handed in | 200 | 0.45 | 4.95 | 4.95 | 345 B | -28% | |
| /v1/stations/recordings | List the station recordings, newest first | 200 | 0.51 | 4.82 | 4.82 | 2 B | -47% | |
| /v1/metrics/forecast | Show the capacity forecast | 200 | 2.86 | 4.43 | 4.43 | 187 B | -57% | |
| /v1/metrics/catalog | Read the metric catalog | 200 | 0.52 | 4.32 | 4.32 | 27.3 KiB | +33% | |
| /v1/metrics/around | Show what changed around a time | 200 | 3.10 | 4.26 | 4.26 | 1.6 KiB | -28% | |
| /v1/tool-library/entry | One tool library entry with its schema, and which seats may use it (path) | 404 | 2.91 | 3.83 | 3.83 | 23 B | -39% | |
| /v1/metrics/alerts | List the alerts and their state | 200 | 2.50 | 3.60 | 3.60 | 2.4 KiB | -49% | |
| /v1/shares | List the share links, all of them or those of one target | 200 | 1.15 | 3.58 | 3.58 | 8.2 KiB | +72% | |
| /v1/tool-library | Browse the tool library: a group's children, or an entry's detail (path) | 200 | 2.54 | 3.56 | 3.56 | 10.4 KiB | -34% | |
| /v1/metrics/slos | List the service-level objectives | 200 | 2.92 | 3.40 | 3.40 | 785 B | -62% | |
| /v1/evals/runs | List the eval records of step sessions, newest first | 200 | 0.86 | 3.37 | 3.37 | 35.9 KiB | -36% | |
| /v1/sessions | List the sessions | 200 | 1.65 | 3.29 | 3.29 | 157.9 KiB | -65% | |
| /v1/vault | Show the vault's state | 200 | 0.46 | 2.61 | 2.61 | 254 B | -38% | |
| /v1/recipe-runs | List recipe runs, newest first, optionally of one task or project | 200 | 1.74 | 2.33 | 2.33 | 26.0 KiB | -59% | |
| /v1/machine | Read this machine's facts | 502 | 0.69 | 2.31 | 2.31 | 101 B | -31% | |
| /v1/foundry/hf/status | Show the Hugging Face status | 200 | 1.86 | 2.20 | 2.20 | 116 B | -52% | |
| /v1/laws | List the laws | 200 | 1.30 | 2.12 | 2.12 | 106.2 KiB | -35% | |
| /v1/provider-catalog | What the Add provider dialog offers: the API kinds and the coding engines | 200 | 0.71 | 2.07 | 2.07 | 10.5 KiB | -45% | |
| /v1/evals/reviewers | Score each reviewer model by its precision (confirmed findings over its findings) and its misses | 200 | 0.61 | 2.04 | 2.04 | 2 B | -61% | |
| /v1/hub | Read the hub's status | 502 | 0.65 | 1.95 | 1.95 | 101 B | -20% | |
| /v1/skills | List the skills, or the archived ones | 200 | 0.83 | 1.92 | 1.92 | 556 B | -49% | |
| /v1/acp/sessions | List the editor (ACP) sessions | 200 | 0.52 | 1.87 | 1.87 | 2 B | -32% | |
| /v1/prompts | List the prompts | 200 | 1.27 | 1.83 | 1.83 | 4.3 KiB | -31% | |
| /v1/prompts/rules | List the prompt rules and templates | 200 | 1.01 | 1.70 | 1.70 | 5.5 KiB | -60% | |
| /v1/skills/rules | List the skill rules and the template | 200 | 0.92 | 1.64 | 1.64 | 2.3 KiB | -61% | |
| /v1/machines | List the machines on this plaza | 200 | 0.60 | 1.55 | 1.55 | 58 B | 0% | |
| /v1/templates | List the warm templates | 502 | 0.65 | 1.54 | 1.54 | 101 B | -48% | |
| /v1/foundry/recipes | List the recipes | 200 | 1.30 | 1.54 | 1.54 | 537 B | +3% | |
| /v1/evals/costs | Report what the step sessions cost and how they did, grouped by project, task, step or model | 200 | 1.25 | 1.51 | 1.51 | 241 B | -52% | |
| /v1/foundry/hf/imports | List the Hugging Face imports | 200 | 1.29 | 1.50 | 1.50 | 14 B | -70% | |
| /v1/connectors | List the connectors and their settings | 200 | 1.11 | 1.48 | 1.48 | 26.8 KiB | -53% | |
| /v1/metrics/dashboards | Read the saved dashboards | 200 | 0.95 | 1.44 | 1.44 | 51 B | -4% | |
| /v1/foundry/datasets | List the datasets | 200 | 1.15 | 1.37 | 1.37 | 1.9 KiB | -38% | |
| /v1/skills/export | Export several skills as one zip | 422 | 0.76 | 1.35 | 1.35 | 24 B | -31% | |
| /v1/projects | List the projects | 200 | 0.64 | 1.33 | 1.33 | 25.6 KiB | -41% | |
| /v1/vault/audit | Read the audit log, verifying it on request | 200 | 0.99 | 1.30 | 1.30 | 30.9 KiB | -59% | |
| /v1/foundry/jobs | List the jobs, newest first | 200 | 0.60 | 1.26 | 1.26 | 2 B | -46% | |
| /v1/ws | Open a socket that streams the hub's events | 400 | 0.60 | 1.23 | 1.23 | 79 B | -53% | |
| /v1/providers | List the providers | 200 | 0.81 | 1.21 | 1.21 | 7.0 KiB | -60% | |
| /v1/metrics/calls/page | Load the next page of calls | 200 | 0.92 | 1.16 | 1.16 | 351 B | -47% | |
| /v1/metrics/settings | Read the metrics settings | 200 | 0.95 | 1.15 | 1.15 | 292 B | -59% | |
| /v1/test-lab | The test lab: its nodes and their capacity, the queue, the running runs and the last runs that ended | 200 | 0.56 | 1.12 | 1.12 | 460 B | -45% | |
| /v1/stations/audit | List the station audit, newest first | 200 | 0.83 | 1.11 | 1.11 | 1.4 KiB | -56% | |
| /v1/mcp/catalog | List the MCP server presets | 200 | 0.55 | 1.06 | 1.06 | 4.8 KiB | -48% | |
| /v1/vault/consumers | List each consumer and its resolved entry | 200 | 0.59 | 1.04 | 1.04 | 5.8 KiB | -53% | |
| /v1/usage/summary | Summarize usage over a time window | 200 | 0.71 | 1.02 | 1.02 | 252 B | -45% | |
| /v1/metrics/scopes | List the metric scopes | 200 | 0.65 | 1.02 | 1.02 | 7.0 KiB | -80% | |
| /v1/fleet | List the machines and the agents on them | 200 | 0.48 | 1.01 | 1.01 | 616 B | -54% | |
| /v1/usage/calls | List the newest model calls | 200 | 0.46 | 1.00 | 1.00 | 41 B | -77% | |
| /v1/stations | List the workstations | 200 | 0.45 | 0.99 | 0.99 | 2 B | -86% | |
| /v1/tool-library/search | Search the tool library by words (q, optionally under a group) | 400 | 0.51 | 0.96 | 0.96 | 25 B | -52% | |
| /v1/platforms | List the git platforms the plaza is authorized on | 200 | 0.45 | 0.95 | 0.95 | 323 B | -53% | |
| /v1/metrics/traces | List the kept traces | 200 | 0.51 | 0.95 | 0.95 | 2 B | -27% | |
| /v1/toolbox | List the toolbox discoveries | 200 | 0.44 | 0.93 | 0.93 | 2 B | -37% | |
| /v1/roles | List the built-in and custom roles | 200 | 0.68 | 0.93 | 0.93 | 44.0 KiB | -69% | |
| /v1/volumes | List the data volumes | 502 | 0.69 | 0.90 | 0.90 | 101 B | -74% | |
| /v1/foundry/devices | List the accelerators and the jobs holding them | 200 | 0.58 | 0.90 | 0.90 | 15 B | -89% | |
| /v1/evals/leaderboard | Rank the scores of a step (or every step) by success rate, then cost per success | 200 | 0.72 | 0.87 | 0.87 | 484 B | -73% | |
| /v1/usage/series | Show usage over time, bucketed | 200 | 0.56 | 0.87 | 0.87 | 28.0 KiB | -90% | |
| /v1/plaza/state | Show whether the plaza is stopped | 200 | 0.41 | 0.84 | 0.84 | 80 B | -49% | |
| /v1/node-link | Read this machine's link to a hub | 200 | 0.44 | 0.84 | 0.84 | 81 B | -66% | |
| /v1/evals/scores | Score the step sessions by step, seat, model and engine | 200 | 0.71 | 0.83 | 0.83 | 484 B | -70% | |
| /v1/acp | Open an editor's ACP socket | 409 | 0.53 | 0.81 | 0.81 | 104 B | -83% | |
| /v1/chats | List the chats | 200 | 0.62 | 0.79 | 0.79 | 7.6 KiB | -69% | |
| /v1/loops | List the scheduled loops | 200 | 0.42 | 0.79 | 0.79 | 2 B | -65% | |
| /v1/login-bridges | The login bridge's switch per vendor: off by default, labelled with the vendor's terms (the owner only) | 200 | 0.56 | 0.78 | 0.78 | 665 B | -65% | |
| /v1/hub-mcp/tokens | List the MCP tokens | 200 | 0.51 | 0.77 | 0.77 | 742 B | -64% | |
| /v1/vault/secrets | List the vault's entries | 200 | 0.50 | 0.75 | 0.75 | 2.2 KiB | -81% | |
| /v1/ide/audit | List the IDE's audit log | 200 | 0.48 | 0.75 | 0.75 | 2 B | -68% | |
| /v1/board | Read the board | 200 | 0.55 | 0.72 | 0.72 | 1.2 KiB | -69% | |
| /v1/connectors/proposals | List connector proposals, newest first | 200 | 0.49 | 0.72 | 0.72 | 2 B | -77% | |
| /v1/runs | List the runs on this machine | 502 | 0.65 | 0.71 | 0.71 | 101 B | -68% | |
| /v1/engines | List the coding engines: the built-in ones and the instances the owner added | 200 | 0.64 | 0.70 | 0.70 | 6.2 KiB | -61% | |
| /v1/cards | List the cards waiting for the owner (and answered ones) | 200 | 0.60 | 0.67 | 0.67 | 14.6 KiB | -66% | |
| /v1/provider-kinds | List the provider kinds you can add | 200 | 0.57 | 0.67 | 0.67 | 8.7 KiB | -48% | |
| /v1/storage | The hub's object store: its blobs and their bytes on disk | 200 | 0.45 | 0.67 | 0.67 | 25 B | -56% | |
| /v1/evals/bench | List the benchmark's replays of archived tasks, newest first | 200 | 0.48 | 0.64 | 0.64 | 221 B | -75% | |
| /v1/approvals | List the approvals, pending and decided | 200 | 0.48 | 0.64 | 0.64 | 1.9 KiB | -62% | |
| /v1/board/export | Export a board thread as Markdown or JSON | 200 | 0.56 | 0.63 | 0.63 | 531 B | -63% | |
| /v1/settings | Get the hub's settings | 200 | 0.50 | 0.61 | 0.61 | 1.2 KiB | -52% | |
| /v1/agents | List the agents | 200 | 0.51 | 0.61 | 0.61 | 1.9 KiB | -63% | |
| /v1/hub-mcp/calls | List the recent MCP calls | 200 | 0.51 | 0.61 | 0.61 | 898 B | -84% | |
| /v1/nodes | List the plaza's machines | 200 | 0.53 | 0.61 | 0.61 | 1.3 KiB | -76% | |
| /v1/mcp/servers | List the MCP servers | 200 | 0.47 | 0.60 | 0.60 | 2 B | -76% | |
| /v1/hub-mcp | Show the hub's MCP server status | 200 | 0.47 | 0.60 | 0.60 | 263 B | -76% | |
| /v1/metrics/events | List the metric events | 200 | 0.46 | 0.60 | 0.60 | 170 B | -76% | |
| /v1/appearance | Read the owner's appearance and saved themes | 200 | 0.44 | 0.59 | 0.59 | 112 B | -85% | |
| /v1/metrics | Scrape the hub's Prometheus metrics | 200 | 0.47 | 0.59 | 0.59 | 5.1 KiB | -68% | |
| /v1/agent-proposals | List the agent proposals | 200 | 0.44 | 0.59 | 0.59 | 2 B | -65% | |
| /v1/laws/rules | List the owner's rules and overrides | 200 | 0.44 | 0.57 | 0.57 | 440 B | -71% | |
| /v1/e2b/sandboxes | List all sandboxes for the owner's console | 200 | 0.42 | 0.57 | 0.57 | 16 B | -55% | |
| /v1/usage-providers | List the usage providers (CLIProxyAPI hubs) and their last readings | 200 | 0.51 | 0.56 | 0.56 | 2 B | -74% | |
| /v1/intake | The intake connections and the items, newest first | 200 | 0.43 | 0.55 | 0.55 | 429 B | -74% | |
| /v1/metrics/calls | List the agents' calls | 200 | 0.47 | 0.55 | 0.55 | 2 B | -84% | |
| /v1/allowances | List every subscription allowance window the usage providers report, with its reset and the subscription models that draw on it | 200 | 0.49 | 0.54 | 0.54 | 2 B | -76% | |
| /v1/tasks | List the tasks | 200 | 0.45 | 0.54 | 0.54 | 6.9 KiB | -80% | |
| /v1/library | List the library's tool versions | 200 | 0.43 | 0.54 | 0.54 | 2 B | -55% | |
| /v1/hub/identity | Show the hub's identity | 200 | 0.44 | 0.54 | 0.54 | 99 B | -88% | |
| /v1/release-channels | The built-in release channels, with their qualification depth and store tracks | 200 | 0.39 | 0.53 | 0.53 | 273 B | -69% | |
| /v1/weft | Show whether Weft, the plaza's version control, is running | 200 | 0.44 | 0.52 | 0.52 | 46 B | -67% | |
| /v1/egress/inject | List the egress injection rules | 200 | 0.44 | 0.51 | 0.51 | 12 B | -70% | |
| /v1/evals/tools | Show each tool's calls, errors and latency (p50, p95) | 200 | 0.44 | 0.50 | 0.50 | 60 B | -64% | |
| /v1/rules/standing | The owner's standing rules: the text in force and its versions | 200 | 0.41 | 0.50 | 0.50 | 72 B | -71% | |
| /v1/stations/link | Open the workstation daemon's link socket | 401 | 0.39 | 0.50 | 0.50 | 61 B | -78% | |
| /v1/decisions/default | Get the default decision model | 200 | 0.45 | 0.49 | 0.49 | 30 B | -86% | |
| /v1/platforms/callback | Take the browser back from a platform | 400 | 0.42 | 0.49 | 0.49 | 501 B | -67% | |
| /v1/e2b/templates | List the templates and their builds | 200 | 0.45 | 0.49 | 0.49 | 16 B | -41% | |
| /v1/stations/codes | List the join codes | 200 | 0.41 | 0.49 | 0.49 | 378 B | -79% | |
| /v1/project-modes | List the modes a project can be in | 200 | 0.43 | 0.48 | 0.48 | 371 B | -68% | |
| /v1/stations/recording-defaults | Show the recording defaults for new workstations | 200 | 0.43 | 0.47 | 0.47 | 74 B | -85% | |
| /v1/auth/check | Check that the session is signed in | 200 | 0.41 | 0.47 | 0.47 | 11 B | -75% | |
| /v1/e2b/keys | List the E2B API keys | 200 | 0.42 | 0.46 | 0.46 | 11 B | -44% | |
| /v1/nodes/codes | List the join codes | 200 | 0.42 | 0.46 | 0.46 | 648 B | -79% | |
| /v1/stations/vms | List the workstation VMs | 200 | 0.40 | 0.45 | 0.45 | 2 B | -78% |
Fuzzing (Schemathesis)
Requests generated from docs/api/openapi.json: a 5xx answer or a connection dropped without an answer is a finding. 502 and 503 come from the scratch hub's missing node daemon and git host. Reported here; findings do not fail the pull request yet.
No findings.
Coverage by area
| Area | Tested | |
|---|---|---|
| projects | 64/70 | |
| internal | 36/42 | |
| e2b | 39/39 | |
| stations | 34/38 | |
| foundry | 32/33 | |
| agents | 27/27 | |
| metrics | 27/27 | |
| skills | 23/23 | |
| nodes | 22/22 | |
| vault | 17/17 | |
| platforms | 17/17 | |
| tasks | 15/15 | |
| ide | 15/15 | |
| prompts | 15/15 | |
| releases | 10/14 | |
| mcp | 13/13 | |
| test-lab | 11/13 | |
| preview | 0/12 | |
| chats | 7/11 | |
| evals | 7/11 | |
| providers | 10/10 | |
| connectors | 8/9 | |
| artifact | 9/9 | |
| laws | 9/9 | |
| library | 8/8 | |
| artifacts | 8/8 | |
| recipes | 8/8 | |
| engines | 7/7 | |
| board | 7/7 | |
| hub-mcp | 6/6 | |
| runs | 5/5 | |
| sessions | 5/5 | |
| files | 5/5 | |
| shares | 5/5 | |
| previews | 5/5 | |
| recipe-runs | 4/5 | |
| intake | 2/5 | |
| usage-providers | 4/4 | |
| appearance | 4/4 | |
| weft | 3/4 | |
| egress | 4/4 | |
| hub | 3/3 | |
| volumes | 3/3 | |
| cards | 3/3 | |
| acp | 3/3 | |
| decisions | 3/3 | |
| toolbox | 3/3 | |
| tool-library | 3/3 | |
| loops | 3/3 | |
| preview-links | 3/3 | |
| plaza | 3/3 | |
| roles | 3/3 | |
| agent-proposals | 3/3 | |
| prices | 3/3 | |
| usage | 3/3 | |
| node-link | 3/3 | |
| auth | 3/3 | |
| console | 3/3 | |
| templates | 2/2 | |
| threads | 2/2 | |
| settings | 2/2 | |
| login-bridges | 2/2 | |
| approvals | 2/2 | |
| storage | 0/2 | |
| ephemeral | 2/2 | |
| reviews | 2/2 | |
| unfurl | 2/2 | |
| rules | 0/2 | |
| overview | 1/1 | |
| machines | 1/1 | |
| machine | 1/1 | |
| ws | 1/1 | |
| provider-kinds | 1/1 | |
| models | 1/1 | |
| provider-catalog | 1/1 | |
| allowances | 1/1 | |
| project-modes | 1/1 | |
| ingest | 1/1 | |
| release-channels | 1/1 | |
| hooks | 1/1 | |
| transfers | 1/1 | |
| fleet | 1/1 |