-
released this
2026-08-24 09:29:42 -05:00 | 89 commits to main since this releaseCanvas is the fifth tenant, and the first whose credential changes under the
running process. C1 ofdocs/canvas-plan.md: the seam, the four rotation rules, the
preflight line naming which credential it booted with, and one tool. Not deployed yet.The institution has disabled personal access token generation on this account, which
closes theAuthorization: Bearerpath every public Canvas MCP server assumes. What is
left is the browser session cookie, and Canvas re-issues it as the session slides.Three of the four rules fail silently
A superseded
canvas_sessionstays valid until its own embedded clock runs out, so a
client that breaks one of these keeps working for hours and then stops for reasons
nothing connects to the break.- Rotate only on a clean 200, and only when a value came back. The point is not
gaining a cookie but not losing one. A 200 carrying noSet-Cookieis the other
half — Canvas re-issues only once the value carries some age, so a client that reads
a missing cookie as failure is broken from its second request. - Follow no redirects. An expired session bounces to the login page, and a client
that follows it gets200and an HTML body, which reads as success. The content type
is checked BEFORE the rotation for the same reason. - One writer — one replica, and a lock inside it.
- The previous value is kept, so a bad write is recoverable without a browser.
Added
list_courses, and deliberately nothing else. C1 proves the credential in
production rather than delivering a surface. It reports a partial page rather than
returning one — Canvas defaults to ten per page, and a client that ignoresLink:
returns the first ten and reports success.- A keepalive, part of the tenant rather than an optimisation. Measured against
superseded values of known age: alive at 10.7 hours, alive at 19.3, dead at 51.6.
A coursework connector is exactly the thing nobody calls over a weekend. docs/operations.mdgained Re-seeding the Canvas session, before deployment
rather than after the first expiry — including the trap that editing
CANVAS_COOKIE_SEEDdoes not re-seed a running tenant, because the jar wins over
the seed whenever it holds anything.deploy/nginx/canvas.conf. What is behind this hostname is an education record.
Testing
All ten new guards were proved by breaking them. The concurrency test caught itself
out first: it asserted "every value sent had been stored at some point", which passed
with the lock removed — a thread sending a stale cookie is still sending a real one.
Rewritten to assert what the lock actually promises, it fails immediately.The transport is
httpx.MockTransportdriving real httpx. Rule 1 turns entirely on what
Response.cookiesdoes with aSet-Cookieheader, so a hand-written fake would be
testing the fake.1349 tests green.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Rotate only on a clean 200, and only when a value came back. The point is not