Skip to content

docs(design): layered service templates + MCP enrollment org-scoping#439

Merged
angel-manuel merged 6 commits into
devfrom
design/corporate-catalog-overlay-and-mcp-scoping
Jul 8, 2026
Merged

docs(design): layered service templates + MCP enrollment org-scoping#439
angel-manuel merged 6 commits into
devfrom
design/corporate-catalog-overlay-and-mcp-scoping

Conversation

@angel-manuel

@angel-manuel angel-manuel commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Two design docs for the corporate-refocus feedback (docs/feedback/2026-07-08-refocus-adapt-to-corporate.md). Docs only — no code changes.

1. layered-service-templates.md — one primitive for forks and overlays

Refined in a design interview (grill-with-docs + domain-modeling); this is a buildable handover spec with a settled model, a migration, an implementation map, and rejected-alternatives called out so they aren't relitigated.

The model. Fork and overlay collapse into one layer = a service_templates row. extends (a base template key) decides its nature: NULLstandalone layer (full OpenAPI doc; the old "fork"); set → derived layer (a sparse delta over a live base). Resolution is a recursive fold: resolve(layer) = apply(delta, resolve(extends)).

  • Masks (restrictive): action allowlist/denylist (monotonic set-ops, no scalar default — it was the only widening lever and the only order-sensitive field), risk clamp-up-only, additive disclose, relabel, template hidden. Monotonic + order-independent ⇒ containment is structural (resolve(child) ⊆ resolve(base)); autodiscover-safe by construction (allowlist excludes new upstream MCP tools, the fix(hubspot): re-sync to HubSpot's replaced MCP tool catalog + RFC 6749 scope fallback on OAuth callback #428 hazard).
  • Extensions (expansive): add new actions + hosts only — no auth changes, no rebinding of base actions (keeps derived layers out of credential resolution). Collision with a base key → reject at write (incl. hidden keys, closing the hide-then-re-add hijack); future upstream collision → base wins + shadowed_extension warning.
  • extends is a LIVE pointer — the entire reason for layers over forks (tracks upstream). Chains global → org → user allowed (extend up); cycle + target guards; cascade cache invalidation.
  • Authority is namespace-based (no classifier in v1): org-namespace layers → admin; user-namespace → user_template_policy enum {none|restrictive|full} (migrated from allow_user_templates; v1 honors none/full; restrictive reserved for the deferred classifier — pure-compute, no future migration). Downgrades forward-only.
  • Validation: blocking write-time delta validation + non-blocking resolution-time resolution_report.warnings[] (recomputed on base change via the live pointer).
  • Feedback point 2 ("prefilled service, each user own OAuth") needs no new mechanism — a curated catalog template users self-instantiate → own instance + own connection.
  • Deferred (reserved): classifier + restrictive tier; scope default-request preset; scope-constraint fold op (waits on per-action required_scopes); materialized openapi cache; proactive new-tools review queue.

Migration 095 adds extends/delta to service_templates and absorbs the existing org/user tier (existing rows are already valid standalone layers). Supersedes the earlier org-catalog-overlay.md draft (renamed).

2. mcp-enrollment-org-scoping.md — corp subdomain onboards only into that org

<org>.{app,api}.overslash.com/mcp must onboard agents only into <org>. Documents the current gap (pending.org_id comes from the session, not the subdomain; DCR clients are org-global) and the fix (derive enrollment org from ctx, re-auth on mismatch, org-scope the fast-path binding, stamp org_id on oauth_mcp_clients). Unchanged in this round.

Follow-ups if approved

  • Layered templates: migration 095 + fold resolver (+ cascade-invalidating cache) in overslash-core; extends/delta repo + validation; effective-template reads in discovery/kernel_create_service/action resolver; user_template_policy; catalog layer-editor UI.
  • MCP scoping: authorize() change + oauth_mcp_clients.org_id migration + tests.

🤖 Generated with Claude Code

Two design docs for the corporate-refocus feedback (2026-07-08):

- org-catalog-overlay.md: an org-owned sparse patch over global and
  autodiscovered templates. Per-action allow/deny, clamp-upward risk,
  disclose, relabel, and instantiation presets, stored as DB JSONB and
  authored as a YAML fragment. Resolved base ⊕ overlay at load so
  discovery, instantiation, and execution all read the effective
  template (hard gate for free). Tracks upstream (no fork); restriction-
  only (never grants capability). Folds in "pre-filled services, each
  user their own OAuth" via publish_mode: user_instantiated.

- mcp-enrollment-org-scoping.md: guarantee that <org>.{app,api}.overslash
  .com/mcp onboards agents only into <org>. Documents the current gap
  (pending.org_id comes from the session not the subdomain; DCR clients
  are org-global) and the fix (derive enrollment org from ctx, re-auth on
  mismatch, org-scope the fast-path binding, stamp org_id on
  oauth_mcp_clients).

Both indexed in docs/design/INDEX.md as Draft — proposed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
overslash Ready Ready Preview, Comment Jul 8, 2026 9:16pm

Request Review

Fold the org-catalog-overlay draft into a single "layered template" model
that unifies forks and overlays, per the design interview.

- Rename org-catalog-overlay.md -> layered-service-templates.md, rewritten
  as a buildable handover spec (glossary, data model + migration 095,
  the recursive fold, delta vocabulary, authority, validation, v1 scope,
  implementation map, rejected-alternatives).
- A layer is a service_templates row; `extends` (NULL=standalone full doc,
  set=derived delta over a base key) resolved by a recursive fold. Masks
  (allowlist/denylist, clamp-up risk, disclose, relabel) are monotonic and
  order-independent -> containment is structural; extensions add
  actions/hosts only (no auth, no rebinding). `extends` is a live pointer.
- Authority is namespace-based; `allow_user_templates` bool generalizes to
  `user_template_policy: none|restrictive|full` (v1 honors none|full).
  Classifier + scope constraints deferred but reserved (no future migration).
- Update docs/design/INDEX.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@angel-manuel angel-manuel changed the title docs(design): org catalog overlay + MCP enrollment org-scoping docs(design): layered service templates + MCP enrollment org-scoping Jul 8, 2026
@angel-manuel angel-manuel marked this pull request as ready for review July 8, 2026 20:57
Comment thread docs/design/mcp-enrollment-org-scoping.md Outdated
The subdomain enrollment lock constrains the subdomain endpoint only — it is
not a claim that an org is reachable solely via its subdomain. Make the root
guarantee explicit: a member with a valid <org> session sees and uses <org>
from app.overslash.com (dashboard + enrollment into their session org),
exactly as today.

- Fix the invariant text that wrongly implied root is only for personal/creator
  orgs; reframe root as the multi-org hub.
- Add "Root and multi-org access" subsection grounded in check_subdomain_matches_jwt
  being a no-op at Root; enrollment org follows the resolved org (subdomain org on
  Org ctx, session org on Root), re-auth-on-mismatch fires on Org ctx only.
- DCR client org_id: NULL for root/back-compat clients (multi-org path); on a
  subdomain accept NULL-or-matching (NULL safe via the org-derivation), reject a
  different-org stamp; root doesn't gate on client org_id.
- Add root multi-org + NULL-client-on-subdomain regression tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-templates

Sentry review catch: the file was renamed; update the cross-reference in
mcp-enrollment-org-scoping.md line 19.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread docs/design/layered-service-templates.md
Comment thread docs/design/mcp-enrollment-org-scoping.md Outdated
Both confirmed against SCHEMA.sql:

- layered-service-templates: migration 095 must DROP NOT NULL on
  service_templates.openapi (derived layers store NULL there) and tighten the
  shape CHECK so standalone→openapi present / derived→delta present. Note the
  ServiceTemplateRow.openapi field becomes Option<serde_json::Value>.

- mcp-enrollment-org-scoping: fast-path binding scoping by org requires
  migrating mcp_client_agent_bindings from UNIQUE (user_identity_id, client_id)
  to UNIQUE (user_identity_id, client_id, org_id) — the current global-unique
  key makes per-org bindings impossible and would collide the root multi-org
  case (one NULL root client enrolling into two orgs a member belongs to).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread docs/design/mcp-enrollment-org-scoping.md
…hange

Sentry follow-on: changing mcp_client_agent_bindings to
UNIQUE(user_identity_id, client_id, org_id) requires matching edits in
mcp_client_agent_binding.rs — upsert ON CONFLICT gains org_id, get_for gains
an org_id param + WHERE clause, and the module doc comment is updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@angel-manuel angel-manuel merged commit 929dec8 into dev Jul 8, 2026
10 checks passed
@angel-manuel angel-manuel deleted the design/corporate-catalog-overlay-and-mcp-scoping branch July 8, 2026 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant