An OpenProject gatekeeper — worth having in-tree? #77
manojitballav
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
I've built a gatekeeper for OpenProject and have been running it
locally against our own instance. Before doing anything more with it I'd rather ask whether it's
something you'd want, since the contributing guidelines are clear that a PR this size isn't the
way to start the conversation.
Branch: https://github.com/manojitballav/cloudflare-os/tree/feat/gatekeeper-openproject
Why a gatekeeper rather than an MCP server
I originally wired OpenProject in through
gatekeeper-mcp, bridging its stdio MCP server to HTTPwith supergateway. That works, and it's what convinced me the native version was worth writing:
MCP_ALLOW_INSECUREfor a loopback endpoint, which disables the SSRF blocklist globally.d.tsThe write story is the part MCP structurally can't reach, and it's the reason the gatekeeper model
seems worth the extra code for a tool people use all day.
What's implemented
Three granularities, whose URL patterns match OpenProject's own URLs so pasting a link from the
browser resolves to the right one:
tsTypehttps://*)OpenProjectSessionhttps://*/projects/:projectId)Projecthttps://*/work_packages/:id)WorkPackageA work package link beats the project path it may be nested under, since
/projects/foo/work_packages/123means the user pointed at one work package. Project-scoped bindings enforce their boundary:
project.workPackage(id)refuses an id from another project rather than quietly widening the grant.Writes — create work package, update fields, comment, log time — are queued and simulated, so an
agent can stack dependent edits instead of stalling on the first prompt. Creation is the one
deliberate exception: a pending work package has no id until OpenProject assigns one, and inventing
one would hand the agent an identifier resolving to nothing, so it isn't simulated and the action
carries
awaitDecision.Updates carry the
lockVersionobserved when the change was queued, so an approval that sat whilesomeone else edited the same work package fails loudly instead of overwriting them. Revert is
implemented for creations, updates and time logs. Comments are not revertable — API v3 has no
endpoint for deleting a journal entry — and the action reports that rather than offering a control
that fails.
Decisions I'd want your read on
to be registered per instance by an admin, which defeats the purpose for someone connecting
their own team's tracker. The cost is that the key carries all of its owner's permissions —
an admin's key is admin access — which the connect form says plainly, but it's a blunter grant
than the OAuth gatekeepers offer.
access is as wide as the key. I kept it because cross-project reporting is a real use case, but
I'd understand an argument for dropping it and making project the coarsest grant.
getAutoApprovableActions()returns[]. Nothing is offered for auto-approval. A commentis arguably benign enough to qualify; I erred toward nothing on a live tracker.
gatekeeper-homeassistant: a single API key with noper-user oracle to verify a prospective observer against, so
addObserveris a no-op.Fit with the repo
It follows
gatekeeper-homeassistantclosely — same auth shape, same file layout, same connectflow. No kernel changes:
run-dev-server.jsdiscovers it by directory prefix, so the packageis entirely self-contained. Unit tests use vitest, matching the other packages.
types:checkandoxlintare clean.What isn't done
query language.
live instance yet; reads are verified end to end against ours.
Happy to take it in a different direction, cut the scope, or keep it out-of-tree entirely if
third-party gatekeepers aren't something you want in the repo yet — in which case a note in the
README about where they should live would be useful on its own.
All reactions