@vantageos/cloud-identity 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,41 @@
3
3
  All notable changes to `@vantageos/cloud-identity` are documented here.
4
4
  This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## [Unreleased] - 0.3.0
7
+
8
+ **BREAKING.** Closes two measured 0.2.0 defects — a right must never be
9
+ granted by absence.
10
+
11
+ ### Changed (breaking)
12
+
13
+ - `passesScopeFilter`, `scopeFilterList`, `scopeFilterGet`, `isMasterScope`,
14
+ `isWildcardScope`: `oauthCtx` is now **mandatory** (`OAuthCtx`, no longer
15
+ `OAuthCtx | undefined`). The 0.2.0 behaviour of an omitted `oauthCtx`
16
+ silently passing every row (wildcard, as if master-scoped) is removed.
17
+ Omitting the argument now breaks the caller's build; at runtime (for
18
+ callers that bypass TypeScript) it throws instead of granting access.
19
+ Callers that deliberately want the old legacy-bearer wildcard behaviour
20
+ must import and pass the new `LEGACY_WILDCARD_CTX` sentinel explicitly.
21
+
22
+ ### Added
23
+
24
+ - `LEGACY_WILDCARD_CTX` — explicit, by-name opt-in for the pre-0.3.0
25
+ legacy-bearer wildcard behaviour (`src/scope-filter.ts`).
26
+ - `requireTenantId(source: TenantSource): string` — organization/workspace
27
+ membership guard, unifying the human (session) and machine (bearer) entry
28
+ paths behind one contract. Refuses on missing session, missing/empty `orgId`, and
29
+ missing/empty bearer-resolved `workspaceId` (`src/org-guard.ts`).
30
+ - `SessionIdentity`, `TenantSource` types.
31
+
32
+ ### Scope note
33
+
34
+ The package now exposes, behind one contract, all four primitives an
35
+ application needs and should never reimplement: tenant resolution
36
+ (`getEffectiveTenantId`), membership requirement (`requireTenantId`), row
37
+ filtering (`passesScopeFilter` / `scopeFilterList` / `scopeFilterGet`), and
38
+ identifier validation (`validateMasterBearer`). Framework adapters (Convex,
39
+ Hono, etc.) wrap these; they must not reimplement the checks.
40
+
6
41
  ## [0.2.0] - 2026-06-13
7
42
 
8
43
  Additive release — domain-tenancy layer. No breaking changes. All 0.1.0 exports unchanged.
@@ -38,5 +73,5 @@ Initial release.
38
73
  - Public type surface: `OAuthCtx`, `ScopeProfile`, `NamespacePrefix`, `FromAllowListEntry`, `ValidateMasterBearerResult`.
39
74
 
40
75
  ### Notes
41
- - `getEffectiveWorkspaceId` deferred to 0.2.0 (depends on `vantageos-crm` workspace helper, owned by Theta).
76
+ - `getEffectiveWorkspaceId` deferred to 0.2.0.
42
77
  - No `provenance: true` in `publishConfig` — provenance only from CI per fleet friction lesson `npm-publish-provenance-only-from-ci-not-local-host`.
package/LICENSE CHANGED
@@ -1,21 +1,110 @@
1
- MIT License
1
+ Functional Source License, Version 1.1, Apache 2.0 Future License
2
2
 
3
- Copyright (c) 2026 VantagePeers Cloud
3
+ Abbreviation
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
5
+ FSL-1.1-Apache-2.0
11
6
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
7
+ Notice
8
+
9
+ Copyright 2026 VantageOS (ElPi Corp)
10
+
11
+ Terms and Conditions
12
+
13
+ Licensor: VantageOS (ElPi Corp)
14
+
15
+ Licensed Work: VantagePeers
16
+ The Licensed Work is (c) 2026 VantageOS (ElPi Corp)
17
+
18
+ Additional Use Grant: You may make production use of the Licensed Work,
19
+ provided Your use does not include offering the Licensed
20
+ Work to third parties as a hosted or managed service
21
+ where the service provides users with access to any
22
+ substantial set of the features or functionality of the
23
+ Licensed Work.
24
+
25
+ Change Date: 2028-04-03 (two years from publication)
26
+
27
+ Change License: Apache License, Version 2.0
28
+
29
+ For information about alternative licensing arrangements for the Licensed Work,
30
+ please contact: contact@vantageos.com
31
+
32
+ License text below is provided for informational purposes only.
33
+
34
+ ---
35
+
36
+ Functional Source License, Version 1.1, Apache 2.0 Future License
37
+
38
+ 1. Purpose
39
+
40
+ This license gives you broad permission to use, modify, and share this
41
+ software, with one important condition: you may not use it to compete with us.
42
+
43
+ 2. Acceptance
44
+
45
+ To use the software, you must agree to these terms. If you do not agree, you
46
+ may not use the software.
47
+
48
+ 3. Copyright License
49
+
50
+ The licensor grants you a non-exclusive, royalty-free, worldwide, non-
51
+ sublicensable, non-transferable license to use, copy, distribute, make
52
+ available, and prepare derivative works of the software, in each case subject
53
+ to the limitations below.
54
+
55
+ 4. Limitations
56
+
57
+ You may not make the functionality of the software available to third parties
58
+ as a service, or otherwise use the software to provide a service to third
59
+ parties that competes with the licensor.
60
+
61
+ 5. Patents
62
+
63
+ The licensor grants you a license, under any patent claims the licensor can
64
+ license or becomes able to license, to make, have made, use, sell, offer for
65
+ sale, import, and have imported the software, in each case subject to the
66
+ limitations and conditions in this license.
67
+
68
+ 6. Fair Use
69
+
70
+ This license is not intended to limit any rights you may have under applicable
71
+ fair use or other laws.
72
+
73
+ 7. No Other Rights
74
+
75
+ These terms do not grant any other rights. The licensor reserves all rights not
76
+ expressly granted.
77
+
78
+ 8. Termination
79
+
80
+ Your license is automatically terminated if you violate the terms. However, if
81
+ you cure the violation within 30 days of discovering it, your license is
82
+ reinstated retroactively.
83
+
84
+ 9. Disclaimer
14
85
 
15
86
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
87
+ IMPLIED.
88
+
89
+ 10. Limitation of Liability
90
+
91
+ TO THE EXTENT PERMITTED BY LAW, THE LICENSOR IS NOT LIABLE FOR ANY DAMAGES
92
+ ARISING FROM THE USE OF THE SOFTWARE.
93
+
94
+ 11. Change Date and License
95
+
96
+ After the Change Date, the licensor grants you the rights under the Change
97
+ License.
98
+
99
+ 12. Definitions
100
+
101
+ "Compete" means providing a product or service that is substantially similar
102
+ to, or a replacement for, all or a significant portion of the Licensed Work.
103
+
104
+ ---
105
+
106
+ Apache License, Version 2.0
107
+
108
+ After the Change Date (2028-04-03), this software will be available under the
109
+ Apache License, Version 2.0. See https://www.apache.org/licenses/LICENSE-2.0
110
+ for full text.
package/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # @vantageos/cloud-identity
2
2
 
3
- Framework-agnostic identity + scope-filter primitives for **VantagePeers Cloud** (multi-tenant MCP).
3
+ Building blocks for multi-tenant authorization: decide which tenant a caller
4
+ belongs to, and keep every other tenant's rows out of the response.
4
5
 
5
- Consumed by `vantage-peers-mcp` and `@vantageos/vantage-crm-mcp` — provides the small, testable building blocks that gate cross-tenant data access in the MCP transport layer.
6
+ If your service holds data for several customers behind one deployment, you
7
+ answer the same four questions on every request — who is calling, which tenant
8
+ they belong to, which rows they may see, and whether their token is genuine.
9
+ This package answers them once, in plain functions with no framework attached,
10
+ so each service does not re-implement (and re-misimplement) its own version.
11
+
12
+ It depends on no web framework, router or database, and runs anywhere
13
+ JavaScript runs.
6
14
 
7
15
  ## Install
8
16
 
@@ -10,182 +18,198 @@ Consumed by `vantage-peers-mcp` and `@vantageos/vantage-crm-mcp` — provides th
10
18
  npm install @vantageos/cloud-identity
11
19
  ```
12
20
 
13
- ## Modules
14
-
15
- | Module | Symbols | Purpose |
16
- | --- | --- | --- |
17
- | `@vantageos/cloud-identity/crypto` | `timingSafeEqual` | Constant-time byte comparison (no branch-timing leak). |
18
- | `@vantageos/cloud-identity/scope-filter` | `passesScopeFilter`, `scopeFilterList`, `scopeFilterGet` | Row-level visibility filter on `(createdBy, namespace)` using the caller's OAuth scope. |
19
- | `@vantageos/cloud-identity/bearer-validation` | `validateMasterBearer` | Bearer-token parsing + sha256 constant-time match against a configured master secret. |
20
- | `@vantageos/cloud-identity/types` | `OAuthCtx`, `ScopeProfile`, `NamespacePrefix`, `FromAllowListEntry`, `ValidateMasterBearerResult` | Public type surface (0.1.0). |
21
- | `@vantageos/cloud-identity/tenancy-domain` | `Workspace`, `WorkspaceMember`, `WorkspaceRole`, `TenantContext`, `workspaceSchema`, `workspaceMemberSchema`, `workspaceRoleSchema`, `tenantContextSchema`, `ScopeViolationError`, `getEffectiveTenantId`, `decodeUnverifiedBearer` | Domain-tenancy layer (0.2.0) — multi-tenant isolation guard + Zod schemas + bearer DECODER (not auth — see security note). |
22
-
23
- ## 0.1.0 scope
24
-
25
- This release ships the **transport-agnostic** core:
26
-
27
- - `crypto.timingSafeEqual`
28
- - `scope-filter.{passesScopeFilter, scopeFilterList, scopeFilterGet}`
29
- - `bearer-validation.validateMasterBearer`
30
- - types
21
+ Node 18 or later. ESM only.
31
22
 
32
- ## 0.2.0 domain-tenancy layer
23
+ ## Quick start
33
24
 
34
- Released 2026-06-13. Additive — no breaking changes.
25
+ ```js
26
+ import { requireTenantId, scopeFilterList } from "@vantageos/cloud-identity";
35
27
 
36
- New in 0.2.0:
37
- - `WorkspaceRole` literal union + Zod schema
38
- - `Workspace` + `WorkspaceMember` + `TenantContext` types + Zod schemas
39
- - `ScopeViolationError` — canonical cross-tenant isolation error
40
- - `getEffectiveTenantId(ctx, args)` — multi-tenant guard (call at the top of every tool handler)
41
- - `decodeUnverifiedBearer(token)` — DECODE-ONLY helper. ⚠️ NOT authentication. NOT a trust boundary. See [Security: bearer decoding](#security-bearer-decoding) below.
28
+ // 1. Which tenant is this caller in? Throws if the answer is "none".
29
+ const tenantId = requireTenantId({
30
+ kind: "session",
31
+ identity: { userId: "user_123", orgId: "org_abc" },
32
+ });
42
33
 
43
- ## 0.3.0 roadmap
44
-
45
- - D1 ghost Clerk identity injection.
46
- - Signed-JWT bearer verification (replacing the decode-only helper as the production trust boundary).
47
- - Co-designed in a Theta cycle.
48
-
49
- ## Usage examples
50
-
51
- ### Constant-time byte comparison
52
-
53
- ```ts
54
- import { timingSafeEqual } from "@vantageos/cloud-identity/crypto";
55
-
56
- const a = new TextEncoder().encode(presentedHash);
57
- const b = new TextEncoder().encode(expectedHash);
58
- if (!(await timingSafeEqual(a, b))) {
59
- return new Response("forbidden", { status: 403 });
60
- }
61
- ```
62
-
63
- ### Scope-aware row filtering
64
-
65
- ```ts
66
- import { scopeFilterList } from "@vantageos/cloud-identity/scope-filter";
67
- import type { OAuthCtx } from "@vantageos/cloud-identity/types";
68
-
69
- const ctx: OAuthCtx = {
70
- fromAllowList: ["alice"],
71
- namespaceReadPrefixes: ["orchestrator/alpha"],
34
+ // 2. Which of these rows may they see?
35
+ const ctx = {
36
+ fromAllowList: ["reports"],
37
+ namespaceReadPrefixes: ["team/finance"],
72
38
  namespaceWritePrefixes: [],
73
39
  };
74
- const visible = scopeFilterList(ctx, rowsFromConvex);
75
- ```
76
40
 
77
- ### Master Bearer validation
41
+ const rows = [
42
+ { createdBy: "reports", namespace: "team/finance" }, // kept
43
+ { createdBy: "someone-else", namespace: "team/legal" }, // dropped
44
+ ];
78
45
 
79
- ```ts
80
- import { validateMasterBearer } from "@vantageos/cloud-identity/bearer-validation";
81
-
82
- const res = await validateMasterBearer(
83
- request.headers.get("authorization") ?? undefined,
84
- process.env.BEARER_SECRET_MASTER ?? "",
85
- );
86
- if (!res.ok) {
87
- return new Response(`unauthorized: ${res.error}`, { status: 401 });
88
- }
46
+ console.log(tenantId); // "org_abc"
47
+ console.log(scopeFilterList(ctx, rows)); // only the first row
89
48
  ```
90
49
 
91
- ### Domain-tenancy layer (0.2.0)
92
-
93
- #### getEffectiveTenantId — multi-tenant isolation guard
50
+ Every symbol is available from the package root. Subpath imports
51
+ (`@vantageos/cloud-identity/scope-filter` and friends) also work if you prefer
52
+ to be explicit about where something comes from.
94
53
 
95
- ```ts
96
- import { getEffectiveTenantId, ScopeViolationError } from "@vantageos/cloud-identity/tenancy-domain";
97
- import type { TenantContext } from "@vantageos/cloud-identity/tenancy-domain";
98
-
99
- // 1. Obtain ctx from your VERIFIED auth middleware. Examples:
100
- // - Clerk session (clerkClient.verifyToken + claims → TenantContext)
101
- // - signed-JWT middleware (jose / jsonwebtoken with public key)
102
- // - Convex tenancy-table lookup against an opaque token
103
- // Whatever path: ctx.workspaceId MUST come from a SIGNATURE-VERIFIED source.
104
- const ctx: TenantContext = await verifiedAuthMiddleware(request);
105
-
106
- // 2. At the top of every tool handler that receives a workspaceId argument:
107
- try {
108
- const tenantId = getEffectiveTenantId(ctx, args);
109
- // tenantId is safe — caller's verified ctx matches the requested workspace
110
- const rows = await db.list({ workspaceId: tenantId });
111
- } catch (err) {
112
- if (err instanceof ScopeViolationError) {
113
- // Translate to 403 — do NOT expose details to caller
114
- return new Response("forbidden", { status: 403 });
115
- }
116
- throw err;
117
- }
118
- ```
54
+ ## Both caller paths, one contract
119
55
 
120
- #### decodeUnverifiedBearer — DECODE-ONLY helper
121
-
122
- > ⚠️ **NOT AUTHENTICATION. NOT A TRUST BOUNDARY.** See the [Security: bearer decoding](#security-bearer-decoding) section below.
56
+ A right is never granted by an absent argument. `requireTenantId` refuses by
57
+ default — a missing session, a missing organization, or a missing
58
+ bearer-resolved workspace all throw; there is no branch that falls through to
59
+ "full access" when tenant information is absent. The same function resolves
60
+ both entry paths a VantagePeers Cloud request can arrive on:
123
61
 
124
62
  ```ts
125
- import { decodeUnverifiedBearer } from "@vantageos/cloud-identity/tenancy-domain";
126
-
127
- // Use cases:
128
- // (a) tests / fixtures
129
- // (b) shape-inspection of a payload that was ALREADY verified upstream
130
- //
131
- // Do NOT feed the returned `workspaceId` into getEffectiveTenantId as the
132
- // trusted `ctx.workspaceId` — the payload is attacker-controlled.
133
-
134
- const token = Buffer.from(JSON.stringify({
135
- userId: "user_alice",
136
- workspaceId: "ws_prod",
137
- roles: ["Admin"],
138
- })).toString("base64");
139
-
140
- const { userId, workspaceId, roles } = await decodeUnverifiedBearer(token);
141
- ```
142
-
143
- #### Zod schemas — validate workspace objects at boundaries
63
+ import {
64
+ requireTenantId,
65
+ decodeUnverifiedBearer,
66
+ type TenantSource,
67
+ } from "@vantageos/cloud-identity";
68
+
69
+ async function resolveTenant(req: {
70
+ session?: { orgId?: string | null } | null;
71
+ bearerToken?: string;
72
+ }): Promise<string> {
73
+ const source: TenantSource = req.session
74
+ ? { kind: "session", identity: req.session }
75
+ : {
76
+ // decodeUnverifiedBearer only decodes the shape — verify the token's
77
+ // signature (or look it up against your own store) before trusting
78
+ // any field on the payload. See "What this package does not do".
79
+ kind: "bearer",
80
+ context: await decodeUnverifiedBearer(req.bearerToken!),
81
+ };
82
+
83
+ // Throws for BOTH paths if no tenant is attached — never returns a
84
+ // "default" tenant id when one is missing.
85
+ return requireTenantId(source);
86
+ }
144
87
 
145
- ```ts
146
- import { workspaceSchema, tenantContextSchema } from "@vantageos/cloud-identity/tenancy-domain";
88
+ // Human path: a signed-in user with an active organization.
89
+ await resolveTenant({ session: { orgId: "org_abc" } }); // -> "org_abc"
147
90
 
148
- // Validate at API boundary
149
- const ws = workspaceSchema.parse(requestBody);
91
+ // Machine path: a caller presenting an already-resolved bearer token.
92
+ await resolveTenant({ bearerToken: someBase64Token }); // -> the token's workspaceId
150
93
 
151
- // Validate TenantContext from an external JWT payload
152
- const ctx = tenantContextSchema.parse(jwtPayload);
94
+ // Either path with nothing attached throws instead of granting access.
95
+ await resolveTenant({ session: null }); // throws Error("Unauthenticated: no session.")
153
96
  ```
154
97
 
155
- ## Security: bearer decoding
156
-
157
- `decodeUnverifiedBearer` performs **no signature verification, no issuer check, no expiry check, no replay-attack protection**. The decoded payload is **attacker-controlled**.
158
-
159
- Forgery example:
160
-
161
- ```ts
162
- // Any attacker can produce this token — no key, no signature required.
163
- const forged = Buffer.from(JSON.stringify({
164
- userId: "x",
165
- workspaceId: "victim-org", // crosses tenant boundary
166
- roles: ["Admin"], // elevates privileges
167
- })).toString("base64");
168
-
169
- const payload = await decodeUnverifiedBearer(forged); // accepts it
98
+ ## What each function does
99
+
100
+ **Tenant resolution and membership**
101
+
102
+ - `requireTenantId(source)` — returns the caller's tenant id, or throws if
103
+ there is none. It accepts both entry paths behind one contract:
104
+ `{ kind: "session", identity }` for a signed-in human, and
105
+ `{ kind: "bearer", context }` for a machine caller. A missing session, a
106
+ missing organization and an empty organization id are all refusals, never a
107
+ default.
108
+ - `getEffectiveTenantId(ctx, args)` — resolves which tenant a request should
109
+ act on when a caller may legitimately act on more than one.
110
+
111
+ **Row filtering**
112
+
113
+ - `passesScopeFilter(ctx, row)` — true when this row is visible to this
114
+ caller.
115
+ - `scopeFilterList(ctx, rows)` — the subset of rows the caller may see.
116
+ - `scopeFilterGet(ctx, row)` — the row, or `null` if the caller may not see it.
117
+ - `isMasterScope(ctx)` / `isWildcardScope(ctx)` — true when the caller holds
118
+ unrestricted access. Useful for skipping filtering you know is pointless;
119
+ never as a substitute for it.
120
+
121
+ Visibility is granted two ways: the row's `createdBy` appears in the caller's
122
+ `fromAllowList`, or the row's `namespace` sits under one of the caller's
123
+ `namespaceReadPrefixes`. Prefixes match on a path boundary, so a caller allowed
124
+ `team/finance` does not thereby see `team/finance-archive`.
125
+
126
+ **Token validation**
127
+
128
+ - `validateMasterBearer(header, secret)` — compares an `Authorization: Bearer`
129
+ header against a shared secret in constant time, and reports *why* it failed:
130
+ header absent, header malformed, or value mismatched.
131
+ - `timingSafeEqual(a, b)` — constant-time byte comparison, if you need to build
132
+ your own check.
133
+
134
+ **Shapes**
135
+
136
+ Zod schemas and their inferred TypeScript types for workspaces, members,
137
+ roles and tenant context: `workspaceSchema` (type `Workspace`),
138
+ `workspaceMemberSchema` (type `WorkspaceMember`), `workspaceRoleSchema`
139
+ (type `WorkspaceRole`), `tenantContextSchema` (type `TenantContext`) — plus
140
+ `ScopeViolationError` (payload type `ScopeViolationPayload`), for refusals you
141
+ want to catch by type, and `BearerPayload`, the type returned by
142
+ `decodeUnverifiedBearer`.
143
+
144
+ **Supporting types** (no runtime code — import with `import type`)
145
+
146
+ - `OAuthCtx` — the context object every scope-filter function above requires:
147
+ `{ fromAllowList, namespaceReadPrefixes, namespaceWritePrefixes, scope? }`.
148
+ - `ScopeProfile`, `NamespacePrefix`, `FromAllowListEntry` — the field types
149
+ that make up `OAuthCtx`.
150
+ - `ValidateMasterBearerResult` — the return type of `validateMasterBearer`.
151
+ - `ScopeFilterable` — the minimal row shape (`{ createdBy?, namespace? }`)
152
+ accepted by `passesScopeFilter` / `scopeFilterList` / `scopeFilterGet`.
153
+ - `SessionIdentity` — the human-path shape `requireTenantId` accepts under
154
+ `{ kind: "session", identity }` (see "Both caller paths, one contract").
155
+ - `TenantSource` — the discriminated union `requireTenantId` accepts:
156
+ `{ kind: "session", identity }` or `{ kind: "bearer", context }`.
157
+
158
+ ## What this package does not do
159
+
160
+ Worth reading before you rely on it.
161
+
162
+ - **`decodeUnverifiedBearer` decodes a token. It does not verify one.** The
163
+ name is literal. It reads the payload out of a bearer token and checks its
164
+ shape; it does not check a signature, so the contents are whatever the caller
165
+ chose to put there. Treating its output as trusted is a vulnerability, not a
166
+ shortcut. Verify the token — a signed JWT, or a lookup against your own store
167
+ — before you believe any field in it.
168
+ - **This is not an authentication system.** There is no user store, no login,
169
+ no session issuance. It takes an identity you have already established and
170
+ tells you what that identity may reach.
171
+ - **It does not talk to your database.** `scopeFilterList` filters rows you
172
+ have already fetched. If fetching them was itself expensive or unsafe, filter
173
+ earlier, in your query.
174
+
175
+ ## Upgrading to 0.3.0
176
+
177
+ **This release changes a default, and it breaks compilation on purpose.**
178
+
179
+ Before 0.3.0, calling a scope-filter function without an authorization context
180
+ returned `true` — no context meant full access. A caller who simply forgot to
181
+ pass the context received every row, and no warning. From 0.3.0 the context is
182
+ required: omitting it is a type error, and passing `null` or `undefined`
183
+ throws.
184
+
185
+ The breakage is deliberate, and it is the point. A caller that stops compiling
186
+ is a caller that has been told. A caller that still compiles and quietly
187
+ returns different rows is an incident nobody attributes to the upgrade.
188
+
189
+ What to do:
190
+
191
+ - If the call site has a real authorization context, pass it. This is almost
192
+ always the right fix, and the context is usually already in scope.
193
+ - If the call site genuinely means "unrestricted", pass the exported constant
194
+ `LEGACY_WILDCARD_CTX` by name. It reproduces the old behaviour exactly. It
195
+ has to be written out, because an intentional bypass belongs in a code review
196
+ and an accidental one should not be possible.
197
+
198
+ ```js
199
+ import { scopeFilterList, LEGACY_WILDCARD_CTX } from "@vantageos/cloud-identity";
200
+
201
+ scopeFilterList(LEGACY_WILDCARD_CTX, rows); // explicit, unrestricted
170
202
  ```
171
203
 
172
- Feeding the decoded `workspaceId` into `getEffectiveTenantId` as the trusted `ctx.workspaceId` silently grants cross-tenant access — the multi-tenant isolation guard collapses because both `ctx` and `args` are then attacker-controlled.
204
+ `requireTenantId` is also new in 0.3.0. Nothing existing calls it, so it breaks
205
+ nothing.
173
206
 
174
- **Use only as**:
175
- - a test fixture / harness helper, OR
176
- - a decoder for a bearer that was ALREADY verified by an upstream signed-JWT or opaque-token middleware (the production trust boundary).
207
+ Version history is in [CHANGELOG.md](./CHANGELOG.md).
177
208
 
178
- **Production trust boundary** is one of:
179
- - Clerk session verification (planned 0.3.0 D1 ghost identity injection)
180
- - signed JWT with rotating issuer key (planned 0.3.0)
181
- - opaque-token lookup against a Convex tenancy table
182
-
183
- Do **not** publish a service that treats `decodeUnverifiedBearer` as authentication.
184
-
185
- ## Security doctrine
209
+ ## License
186
210
 
187
- Canonical reference: `docs/cloud/security-multi-tenant.md` in the `vantage-peers` repo.
211
+ FSL-1.1-Apache-2.0. See [LICENSE](./LICENSE).
188
212
 
189
- ## License
213
+ ## Issues
190
214
 
191
- MIT — VantagePeers Cloud, 2026.
215
+ https://github.com/vantageos-agency/cloud-identity/issues
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Master-token Bearer validation.
3
3
  *
4
- * Extracted from the master-gate logic in
5
- * `vantage-memory/mcp-server/server-http.ts` (`masterOnlyMiddleware`) and
6
- * HARDENED per @vantageos/cloud-identity 0.1.0 contract:
4
+ * Extracted from an earlier in-tree master-gate middleware and HARDENED per
5
+ * this package's contract:
7
6
  * - SHA-256 hash both the presented token AND the configured master secret,
8
7
  * then compare the two digests with `timingSafeEqual` (constant time).
9
8
  * - The original middleware did a direct `token !== masterToken` string
@@ -1 +1 @@
1
- {"version":3,"file":"bearer-validation.d.ts","sourceRoot":"","sources":["../src/bearer-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAU7D;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,0BAA0B,CAAC,CAsBrC"}
1
+ {"version":3,"file":"bearer-validation.d.ts","sourceRoot":"","sources":["../src/bearer-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAU7D;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,0BAA0B,CAAC,CAsBrC"}
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Master-token Bearer validation.
3
3
  *
4
- * Extracted from the master-gate logic in
5
- * `vantage-memory/mcp-server/server-http.ts` (`masterOnlyMiddleware`) and
6
- * HARDENED per @vantageos/cloud-identity 0.1.0 contract:
4
+ * Extracted from an earlier in-tree master-gate middleware and HARDENED per
5
+ * this package's contract:
7
6
  * - SHA-256 hash both the presented token AND the configured master secret,
8
7
  * then compare the two digests with `timingSafeEqual` (constant time).
9
8
  * - The original middleware did a direct `token !== masterToken` string
@@ -1 +1 @@
1
- {"version":3,"file":"bearer-validation.js","sourceRoot":"","sources":["../src/bearer-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAEtC,KAAK,UAAU,WAAW,CAAC,KAAa;IACtC,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAA8B,EAC9B,YAAoB;IAEpB,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;QACzE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACzC,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IACD,sEAAsE;IACtE,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAClE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACpD,WAAW,CAAC,KAAK,CAAC;QAClB,WAAW,CAAC,YAAY,CAAC;KAC1B,CAAC,CAAC;IACH,MAAM,EAAE,GAAG,MAAM,eAAe,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAC5D,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AAC9D,CAAC"}
1
+ {"version":3,"file":"bearer-validation.js","sourceRoot":"","sources":["../src/bearer-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAEtC,KAAK,UAAU,WAAW,CAAC,KAAa;IACtC,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAA8B,EAC9B,YAAoB;IAEpB,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;QACzE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACzC,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IACD,sEAAsE;IACtE,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAClE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACpD,WAAW,CAAC,KAAK,CAAC;QAClB,WAAW,CAAC,YAAY,CAAC;KAC1B,CAAC,CAAC;IACH,MAAM,EAAE,GAAG,MAAM,eAAe,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAC5D,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AAC9D,CAAC"}
package/dist/crypto.d.ts CHANGED
@@ -1,11 +1,9 @@
1
1
  /**
2
2
  * Constant-time byte-array comparison.
3
3
  *
4
- * Ported from `vantage-memory/mcp-server/src/crypto.ts` (Day 47 master-token
5
- * gate, Eta F1 MAJOR PR #621). Surface adapted from `(string, string)` to
6
- * `(Uint8Array, Uint8Array)` per @vantageos/cloud-identity 0.1.0 contract so
7
- * the helper is framework-agnostic and reusable by any caller (hash bytes,
8
- * raw token bytes, HMAC tags, etc.).
4
+ * Takes `(Uint8Array, Uint8Array)` rather than strings so the helper stays
5
+ * framework-agnostic and reusable by any caller: hash bytes, raw token
6
+ * bytes, HMAC tags.
9
7
  *
10
8
  * Algorithm (identical to source):
11
9
  * 1. Length mismatch → still run a dummy Web Crypto HMAC over equal-length
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,CAAC,EAAE,UAAU,EACb,CAAC,EAAE,UAAU,GACZ,OAAO,CAAC,OAAO,CAAC,CAoBlB"}
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,CAAC,EAAE,UAAU,EACb,CAAC,EAAE,UAAU,GACZ,OAAO,CAAC,OAAO,CAAC,CAoBlB"}
package/dist/crypto.js CHANGED
@@ -1,11 +1,9 @@
1
1
  /**
2
2
  * Constant-time byte-array comparison.
3
3
  *
4
- * Ported from `vantage-memory/mcp-server/src/crypto.ts` (Day 47 master-token
5
- * gate, Eta F1 MAJOR PR #621). Surface adapted from `(string, string)` to
6
- * `(Uint8Array, Uint8Array)` per @vantageos/cloud-identity 0.1.0 contract so
7
- * the helper is framework-agnostic and reusable by any caller (hash bytes,
8
- * raw token bytes, HMAC tags, etc.).
4
+ * Takes `(Uint8Array, Uint8Array)` rather than strings so the helper stays
5
+ * framework-agnostic and reusable by any caller: hash bytes, raw token
6
+ * bytes, HMAC tags.
9
7
  *
10
8
  * Algorithm (identical to source):
11
9
  * 1. Length mismatch → still run a dummy Web Crypto HMAC over equal-length
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,CAAa,EACb,CAAa;IAEb,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QAC1B,6EAA6E;QAC7E,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACxC,KAAK,EACL,WAAsC,EACtC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAC;QACF,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAgC,CAAC,CAAC;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,CAAa,EACb,CAAa;IAEb,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QAC1B,6EAA6E;QAC7E,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACxC,KAAK,EACL,WAAsC,EACtC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAC;QACF,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAgC,CAAC,CAAC;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,CAAC;AACpB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  /**
2
2
  * @vantageos/cloud-identity — public surface.
3
3
  *
4
- * Framework-agnostic identity + scope-filter primitives for VantagePeers Cloud
5
- * (multi-tenant MCP). Consumed by `vantage-peers-mcp` and
6
- * `@vantageos/vantage-crm-mcp`.
4
+ * Framework-agnostic building blocks for multi-tenant authorization:
5
+ * tenant resolution, membership requirement, row filtering and bearer
6
+ * validation.
7
7
  */
8
8
  export { timingSafeEqual } from "./crypto.js";
9
- export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, type ScopeFilterable, } from "./scope-filter.js";
9
+ export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, LEGACY_WILDCARD_CTX, type ScopeFilterable, } from "./scope-filter.js";
10
10
  export { validateMasterBearer } from "./bearer-validation.js";
11
11
  export type { OAuthCtx, ScopeProfile, NamespacePrefix, FromAllowListEntry, ValidateMasterBearerResult, } from "./types.js";
12
12
  export { workspaceSchema, workspaceMemberSchema, workspaceRoleSchema, tenantContextSchema, ScopeViolationError, getEffectiveTenantId, decodeUnverifiedBearer, } from "./tenancy-domain.js";
13
13
  export type { Workspace, WorkspaceMember, WorkspaceRole, TenantContext, ScopeViolationPayload, BearerPayload, } from "./tenancy-domain.js";
14
+ export { requireTenantId } from "./org-guard.js";
15
+ export type { SessionIdentity, TenantSource } from "./org-guard.js";
14
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,aAAa,GACd,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * @vantageos/cloud-identity — public surface.
3
3
  *
4
- * Framework-agnostic identity + scope-filter primitives for VantagePeers Cloud
5
- * (multi-tenant MCP). Consumed by `vantage-peers-mcp` and
6
- * `@vantageos/vantage-crm-mcp`.
4
+ * Framework-agnostic building blocks for multi-tenant authorization:
5
+ * tenant resolution, membership requirement, row filtering and bearer
6
+ * validation.
7
7
  */
8
8
  export { timingSafeEqual } from "./crypto.js";
9
- export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, } from "./scope-filter.js";
9
+ export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, LEGACY_WILDCARD_CTX, } from "./scope-filter.js";
10
10
  export { validateMasterBearer } from "./bearer-validation.js";
11
11
  // 0.2.0 — Domain-tenancy layer (additive)
12
12
  export { workspaceSchema, workspaceMemberSchema, workspaceRoleSchema, tenantContextSchema, ScopeViolationError, getEffectiveTenantId, decodeUnverifiedBearer, } from "./tenancy-domain.js";
13
+ // Organization-membership guard (unified session + bearer contract)
14
+ export { requireTenantId } from "./org-guard.js";
13
15
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAS9D,0CAA0C;AAC1C,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAS9D,0CAA0C;AAC1C,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAU7B,oEAAoE;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @vantageos/cloud-identity — Organization-membership guard.
3
+ * (See package.json "version" for the release this shipped in.)
4
+ *
5
+ * Closes the second measured 0.2.0 defect: the published package exposed no
6
+ * "must belong to an organization" gate anywhere (`grep -rlE
7
+ * "requireOrg|orgRequired|orgId" package/dist/*.js` returned no matches on
8
+ * the 0.2.0 tarball). Absence of an organization must REFUSE, never fall
9
+ * through to full access — the same defect class as the scope-filter fix in
10
+ * `./scope-filter.ts`.
11
+ *
12
+ * The semantics are reused verbatim from a Convex implementation of the same
13
+ * guard rather than reinvented — a second implementation of one authorization
14
+ * check is a second chance to get it wrong. That original:
15
+ *
16
+ * - takes a Convex `QueryCtx | MutationCtx | ActionCtx`
17
+ * - derives identity via `ctx.auth.getUserIdentity()`
18
+ * - throws when there is no session
19
+ * - throws when `identity.org_id` is not a non-empty string
20
+ * - returns the org id
21
+ *
22
+ * ADAPTATION (named per the brief's requirement to flag any semantic
23
+ * deviation): this package is framework-agnostic and has no `ctx.auth`. The
24
+ * caller pre-resolves whichever identity/bearer object it already has
25
+ * (Clerk session identity for the human path, or the package's own
26
+ * `TenantContext` — see `./tenancy-domain.ts` — for the machine/bearer path)
27
+ * and passes it in as a `TenantSource`. The refuse-on-absence /
28
+ * refuse-on-empty semantics are otherwise identical to the source function;
29
+ * no other behavioural change was made.
30
+ *
31
+ * This is the membership half of the package's contract; tenant resolution,
32
+ * row filtering and identifier validation are covered by
33
+ * `getEffectiveTenantId`, `passesScopeFilter`/`scopeFilterList` and
34
+ * `validateMasterBearer` respectively. Framework adapters wrap
35
+ * `requireTenantId`; they must never reimplement its checks.
36
+ */
37
+ import type { TenantContext } from "./tenancy-domain.js";
38
+ /**
39
+ * Minimal shape of a resolved human-path session identity. Intentionally
40
+ * framework-agnostic: the caller derives this from Clerk (or any other
41
+ * session provider) before calling `requireTenantId` — this package never
42
+ * imports a session SDK.
43
+ */
44
+ export type SessionIdentity = {
45
+ orgId?: string | null;
46
+ };
47
+ /**
48
+ * Discriminated union covering both VantagePeers Cloud entry paths behind
49
+ * one contract:
50
+ * - `session`: human path, via a connection/login provider (Clerk, etc.).
51
+ * - `bearer`: machine path, via a resolved, ALREADY-VERIFIED token
52
+ * (`TenantContext` — see `decodeUnverifiedBearer` / the planned signed-JWT
53
+ * verifier for how a `TenantContext` gets produced).
54
+ */
55
+ export type TenantSource = {
56
+ kind: "session";
57
+ identity: SessionIdentity | null | undefined;
58
+ } | {
59
+ kind: "bearer";
60
+ context: TenantContext;
61
+ };
62
+ /**
63
+ * Resolve the caller's tenant id, refusing when no organization/workspace is
64
+ * attached — for EITHER entry path, behind one contract.
65
+ *
66
+ * - `kind: "session"` and `identity` is null/undefined → throws
67
+ * ("Unauthenticated: no session.")
68
+ * - `kind: "session"` and `identity.orgId` is missing or empty → throws
69
+ * ("No active organization on this session...")
70
+ * - `kind: "bearer"` and `context.workspaceId` is missing or empty → throws
71
+ * ("No workspace on this bearer-resolved tenant context...")
72
+ * - otherwise → returns the tenant id (orgId, or workspaceId)
73
+ *
74
+ * A right is never granted by absence: every branch above refuses by
75
+ * throwing, there is no fallback branch that returns a value when
76
+ * organization/workspace information is missing.
77
+ */
78
+ export declare function requireTenantId(source: TenantSource): string;
79
+ //# sourceMappingURL=org-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-guard.d.ts","sourceRoot":"","sources":["../src/org-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAqB5D"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @vantageos/cloud-identity — Organization-membership guard.
3
+ * (See package.json "version" for the release this shipped in.)
4
+ *
5
+ * Closes the second measured 0.2.0 defect: the published package exposed no
6
+ * "must belong to an organization" gate anywhere (`grep -rlE
7
+ * "requireOrg|orgRequired|orgId" package/dist/*.js` returned no matches on
8
+ * the 0.2.0 tarball). Absence of an organization must REFUSE, never fall
9
+ * through to full access — the same defect class as the scope-filter fix in
10
+ * `./scope-filter.ts`.
11
+ *
12
+ * The semantics are reused verbatim from a Convex implementation of the same
13
+ * guard rather than reinvented — a second implementation of one authorization
14
+ * check is a second chance to get it wrong. That original:
15
+ *
16
+ * - takes a Convex `QueryCtx | MutationCtx | ActionCtx`
17
+ * - derives identity via `ctx.auth.getUserIdentity()`
18
+ * - throws when there is no session
19
+ * - throws when `identity.org_id` is not a non-empty string
20
+ * - returns the org id
21
+ *
22
+ * ADAPTATION (named per the brief's requirement to flag any semantic
23
+ * deviation): this package is framework-agnostic and has no `ctx.auth`. The
24
+ * caller pre-resolves whichever identity/bearer object it already has
25
+ * (Clerk session identity for the human path, or the package's own
26
+ * `TenantContext` — see `./tenancy-domain.ts` — for the machine/bearer path)
27
+ * and passes it in as a `TenantSource`. The refuse-on-absence /
28
+ * refuse-on-empty semantics are otherwise identical to the source function;
29
+ * no other behavioural change was made.
30
+ *
31
+ * This is the membership half of the package's contract; tenant resolution,
32
+ * row filtering and identifier validation are covered by
33
+ * `getEffectiveTenantId`, `passesScopeFilter`/`scopeFilterList` and
34
+ * `validateMasterBearer` respectively. Framework adapters wrap
35
+ * `requireTenantId`; they must never reimplement its checks.
36
+ */
37
+ /**
38
+ * Resolve the caller's tenant id, refusing when no organization/workspace is
39
+ * attached — for EITHER entry path, behind one contract.
40
+ *
41
+ * - `kind: "session"` and `identity` is null/undefined → throws
42
+ * ("Unauthenticated: no session.")
43
+ * - `kind: "session"` and `identity.orgId` is missing or empty → throws
44
+ * ("No active organization on this session...")
45
+ * - `kind: "bearer"` and `context.workspaceId` is missing or empty → throws
46
+ * ("No workspace on this bearer-resolved tenant context...")
47
+ * - otherwise → returns the tenant id (orgId, or workspaceId)
48
+ *
49
+ * A right is never granted by absence: every branch above refuses by
50
+ * throwing, there is no fallback branch that returns a value when
51
+ * organization/workspace information is missing.
52
+ */
53
+ export function requireTenantId(source) {
54
+ if (source.kind === "session") {
55
+ if (!source.identity) {
56
+ throw new Error("Unauthenticated: no session.");
57
+ }
58
+ const orgId = source.identity.orgId;
59
+ if (typeof orgId !== "string" || orgId.length === 0) {
60
+ throw new Error("No active organization on this session: select an organization before accessing agents.");
61
+ }
62
+ return orgId;
63
+ }
64
+ const workspaceId = source.context?.workspaceId;
65
+ if (typeof workspaceId !== "string" || workspaceId.length === 0) {
66
+ throw new Error("No workspace on this bearer-resolved tenant context: the bearer must resolve to a non-empty workspaceId.");
67
+ }
68
+ return workspaceId;
69
+ }
70
+ //# sourceMappingURL=org-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-guard.js","sourceRoot":"","sources":["../src/org-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AA0BH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC;IAChD,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CACb,0GAA0G,CAC3G,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -1,18 +1,24 @@
1
1
  /**
2
- * Sprint S3.1 B2 — scope-aware filter helpers for VP MCP tools.
2
+ * Scope-aware row filter.
3
3
  *
4
- * Ported from `vantage-memory/mcp-server/src/scope-filter.ts`. Behaviour is
5
- * preserved verbatim; only the type dependency on the in-tree `OAuthContext`
6
- * is replaced by the framework-agnostic `OAuthCtx` from `./types`.
4
+ * The behaviour was preserved verbatim from an earlier in-tree
5
+ * implementation; the only change was replacing a framework-specific context
6
+ * type with the framework-agnostic `OAuthCtx` from `./types`.
7
7
  *
8
- * Doctrine references:
9
- * - decisions/doctrine-scope-aware-filter-2026-05-26.md (D3 base)
10
- * - memory j579y6f31g7xzgtgdnpgetdmjx87ztyj (D9-D14 extension)
11
- *
12
- * Contract:
8
+ * Contract (0.3.0 — BREAKING vs. 0.2.0):
9
+ * - `oauthCtx` is now MANDATORY. A right is never granted by absence: the
10
+ * 0.2.0 behaviour of `oauthCtx === undefined` silently passing everything
11
+ * (as if the caller were master-scoped) was the exact defect class this
12
+ * release closes. Every exported function below now requires `OAuthCtx`
13
+ * at the type level — an omitted argument breaks the caller's build,
14
+ * it never falls through to a more permissive runtime path.
15
+ * - Callers that genuinely need the old legacy-bearer wildcard behaviour
16
+ * MUST import and pass `LEGACY_WILDCARD_CTX` explicitly — the compat
17
+ * path is requested by name, never inferred from a missing argument.
18
+ * - As defense-in-depth against callers that bypass the type system
19
+ * (untyped JS, `as any`), every function also throws at runtime if
20
+ * `oauthCtx` is nullish, instead of silently granting access.
13
21
  * - Master scope (scope === "master" OR fromAllowList includes "*") = wildcard pass.
14
- * - Legacy bearer (oauthCtx === undefined) = wildcard pass (treated as master-
15
- * equivalent for backward-compatibility with mcpTenants Pi/Tau/Phi paths).
16
22
  * - Non-master scope = row passes iff:
17
23
  * row.createdBy ∈ oauthCtx.fromAllowList
18
24
  * OR
@@ -24,6 +30,12 @@
24
30
  * imports — to keep the helpers trivially unit-testable.
25
31
  */
26
32
  import type { OAuthCtx } from "./types.js";
33
+ /**
34
+ * Explicit legacy-compat sentinel. Passing this constant reproduces the
35
+ * pre-0.3.0 "no oauthCtx" wildcard behaviour — but the caller must import
36
+ * and pass it BY NAME. It is never inferred from an omitted argument.
37
+ */
38
+ export declare const LEGACY_WILDCARD_CTX: OAuthCtx;
27
39
  /**
28
40
  * Row shape accepted by the scope filter. All fields optional because real
29
41
  * Convex documents from list_peers / list_messages / etc. don't all carry both.
@@ -37,35 +49,38 @@ export type ScopeFilterable = {
37
49
  * Mirrors `isMasterScope` from the source module: `scope === "master"` OR
38
50
  * `fromAllowList` contains the `"*"` sentinel.
39
51
  */
40
- export declare function isMasterScope(ctx: OAuthCtx | undefined): boolean;
52
+ export declare function isMasterScope(ctx: OAuthCtx): boolean;
41
53
  /**
42
54
  * Core predicate. Returns true when the row is visible to the caller.
43
55
  *
44
56
  * - Master scope (isMasterScope === true) → true (wildcard)
45
- * - Legacy bearer (oauthCtx === undefined) → true (back-compat)
46
57
  * - row.createdBy ∈ oauthCtx.fromAllowList → true
47
58
  * - row.namespace === prefix OR startsWith prefix+'/'
48
59
  * for any prefix ∈ oauthCtx.namespaceReadPrefixes → true
49
60
  * - otherwise → false
50
61
  *
62
+ * `oauthCtx` is mandatory (see module-level contract note above): an omitted
63
+ * argument throws instead of silently granting wildcard access. Callers that
64
+ * need the old legacy-bearer wildcard behaviour must pass `LEGACY_WILDCARD_CTX`
65
+ * explicitly.
66
+ *
51
67
  * Substring matches that don't fall on a '/' boundary are explicitly rejected
52
68
  * (e.g. namespace="orchestrator/alphabet" does NOT match prefix
53
69
  * "orchestrator/alpha"). This avoids the classic prefix-isolation bypass.
54
70
  */
55
- export declare function passesScopeFilter(oauthCtx: OAuthCtx | undefined, row: ScopeFilterable): boolean;
71
+ export declare function passesScopeFilter(oauthCtx: OAuthCtx, row: ScopeFilterable): boolean;
56
72
  /**
57
73
  * Filter a list of rows (post-query). Used by list_* tools.
58
74
  */
59
- export declare function scopeFilterList<T extends ScopeFilterable>(oauthCtx: OAuthCtx | undefined, rows: T[]): T[];
75
+ export declare function scopeFilterList<T extends ScopeFilterable>(oauthCtx: OAuthCtx, rows: T[]): T[];
60
76
  /**
61
77
  * Assert a single row passes (get_* tools). Returns the row when allowed, null
62
78
  * otherwise — callers translate null to a 404-equivalent "not found" MCP error
63
79
  * to avoid leaking the difference between "absent" and "filtered out".
64
80
  */
65
- export declare function scopeFilterGet<T extends ScopeFilterable>(oauthCtx: OAuthCtx | undefined, row: T | null | undefined): T | null;
81
+ export declare function scopeFilterGet<T extends ScopeFilterable>(oauthCtx: OAuthCtx, row: T | null | undefined): T | null;
66
82
  /**
67
83
  * Helper for callers that want a single "is master / legacy" predicate.
68
- * Mirrors the legacy-bearer-passes-through convention.
69
84
  */
70
- export declare function isWildcardScope(ctx: OAuthCtx | undefined): boolean;
85
+ export declare function isWildcardScope(ctx: OAuthCtx): boolean;
71
86
  //# sourceMappingURL=scope-filter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scope-filter.d.ts","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAIhE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,GAAG,EAAE,eAAe,GACnB,OAAO,CAYT;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,eAAe,EACvD,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,IAAI,EAAE,CAAC,EAAE,GACR,CAAC,EAAE,CAEL;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,eAAe,EACtD,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACxB,CAAC,GAAG,IAAI,CAGV;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAGlE"}
1
+ {"version":3,"file":"scope-filter.d.ts","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAKjC,CAAC;AAWF;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAIpD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,eAAe,GACnB,OAAO,CAYT;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,eAAe,EACvD,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EAAE,GACR,CAAC,EAAE,CAGL;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,eAAe,EACtD,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACxB,CAAC,GAAG,IAAI,CAIV;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAGtD"}
@@ -1,18 +1,24 @@
1
1
  /**
2
- * Sprint S3.1 B2 — scope-aware filter helpers for VP MCP tools.
2
+ * Scope-aware row filter.
3
3
  *
4
- * Ported from `vantage-memory/mcp-server/src/scope-filter.ts`. Behaviour is
5
- * preserved verbatim; only the type dependency on the in-tree `OAuthContext`
6
- * is replaced by the framework-agnostic `OAuthCtx` from `./types`.
4
+ * The behaviour was preserved verbatim from an earlier in-tree
5
+ * implementation; the only change was replacing a framework-specific context
6
+ * type with the framework-agnostic `OAuthCtx` from `./types`.
7
7
  *
8
- * Doctrine references:
9
- * - decisions/doctrine-scope-aware-filter-2026-05-26.md (D3 base)
10
- * - memory j579y6f31g7xzgtgdnpgetdmjx87ztyj (D9-D14 extension)
11
- *
12
- * Contract:
8
+ * Contract (0.3.0 — BREAKING vs. 0.2.0):
9
+ * - `oauthCtx` is now MANDATORY. A right is never granted by absence: the
10
+ * 0.2.0 behaviour of `oauthCtx === undefined` silently passing everything
11
+ * (as if the caller were master-scoped) was the exact defect class this
12
+ * release closes. Every exported function below now requires `OAuthCtx`
13
+ * at the type level — an omitted argument breaks the caller's build,
14
+ * it never falls through to a more permissive runtime path.
15
+ * - Callers that genuinely need the old legacy-bearer wildcard behaviour
16
+ * MUST import and pass `LEGACY_WILDCARD_CTX` explicitly — the compat
17
+ * path is requested by name, never inferred from a missing argument.
18
+ * - As defense-in-depth against callers that bypass the type system
19
+ * (untyped JS, `as any`), every function also throws at runtime if
20
+ * `oauthCtx` is nullish, instead of silently granting access.
13
21
  * - Master scope (scope === "master" OR fromAllowList includes "*") = wildcard pass.
14
- * - Legacy bearer (oauthCtx === undefined) = wildcard pass (treated as master-
15
- * equivalent for backward-compatibility with mcpTenants Pi/Tau/Phi paths).
16
22
  * - Non-master scope = row passes iff:
17
23
  * row.createdBy ∈ oauthCtx.fromAllowList
18
24
  * OR
@@ -23,14 +29,30 @@
23
29
  * NOTE: this module is intentionally framework-agnostic — no McpServer / Hono
24
30
  * imports — to keep the helpers trivially unit-testable.
25
31
  */
32
+ /**
33
+ * Explicit legacy-compat sentinel. Passing this constant reproduces the
34
+ * pre-0.3.0 "no oauthCtx" wildcard behaviour — but the caller must import
35
+ * and pass it BY NAME. It is never inferred from an omitted argument.
36
+ */
37
+ export const LEGACY_WILDCARD_CTX = {
38
+ scope: "master",
39
+ fromAllowList: ["*"],
40
+ namespaceReadPrefixes: [],
41
+ namespaceWritePrefixes: [],
42
+ };
43
+ function assertOauthCtx(oauthCtx) {
44
+ if (oauthCtx == null) {
45
+ throw new TypeError("oauthCtx is required — pass an explicit OAuthCtx, or LEGACY_WILDCARD_CTX " +
46
+ "if you deliberately want the pre-0.3.0 legacy-bearer wildcard behaviour.");
47
+ }
48
+ }
26
49
  /**
27
50
  * Returns true when the scope profile grants full, wildcard access.
28
51
  * Mirrors `isMasterScope` from the source module: `scope === "master"` OR
29
52
  * `fromAllowList` contains the `"*"` sentinel.
30
53
  */
31
54
  export function isMasterScope(ctx) {
32
- if (!ctx)
33
- return false;
55
+ assertOauthCtx(ctx);
34
56
  if (ctx.scope === "master")
35
57
  return true;
36
58
  return ctx.fromAllowList.includes("*");
@@ -39,19 +61,22 @@ export function isMasterScope(ctx) {
39
61
  * Core predicate. Returns true when the row is visible to the caller.
40
62
  *
41
63
  * - Master scope (isMasterScope === true) → true (wildcard)
42
- * - Legacy bearer (oauthCtx === undefined) → true (back-compat)
43
64
  * - row.createdBy ∈ oauthCtx.fromAllowList → true
44
65
  * - row.namespace === prefix OR startsWith prefix+'/'
45
66
  * for any prefix ∈ oauthCtx.namespaceReadPrefixes → true
46
67
  * - otherwise → false
47
68
  *
69
+ * `oauthCtx` is mandatory (see module-level contract note above): an omitted
70
+ * argument throws instead of silently granting wildcard access. Callers that
71
+ * need the old legacy-bearer wildcard behaviour must pass `LEGACY_WILDCARD_CTX`
72
+ * explicitly.
73
+ *
48
74
  * Substring matches that don't fall on a '/' boundary are explicitly rejected
49
75
  * (e.g. namespace="orchestrator/alphabet" does NOT match prefix
50
76
  * "orchestrator/alpha"). This avoids the classic prefix-isolation bypass.
51
77
  */
52
78
  export function passesScopeFilter(oauthCtx, row) {
53
- if (!oauthCtx)
54
- return true;
79
+ assertOauthCtx(oauthCtx);
55
80
  if (isMasterScope(oauthCtx))
56
81
  return true;
57
82
  const { createdBy, namespace } = row;
@@ -71,6 +96,7 @@ export function passesScopeFilter(oauthCtx, row) {
71
96
  * Filter a list of rows (post-query). Used by list_* tools.
72
97
  */
73
98
  export function scopeFilterList(oauthCtx, rows) {
99
+ assertOauthCtx(oauthCtx);
74
100
  return rows.filter((r) => passesScopeFilter(oauthCtx, r));
75
101
  }
76
102
  /**
@@ -79,17 +105,16 @@ export function scopeFilterList(oauthCtx, rows) {
79
105
  * to avoid leaking the difference between "absent" and "filtered out".
80
106
  */
81
107
  export function scopeFilterGet(oauthCtx, row) {
108
+ assertOauthCtx(oauthCtx);
82
109
  if (row == null)
83
110
  return null;
84
111
  return passesScopeFilter(oauthCtx, row) ? row : null;
85
112
  }
86
113
  /**
87
114
  * Helper for callers that want a single "is master / legacy" predicate.
88
- * Mirrors the legacy-bearer-passes-through convention.
89
115
  */
90
116
  export function isWildcardScope(ctx) {
91
- if (!ctx)
92
- return true;
117
+ assertOauthCtx(ctx);
93
118
  return isMasterScope(ctx);
94
119
  }
95
120
  //# sourceMappingURL=scope-filter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scope-filter.js","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAaH;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAyB;IACrD,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,IAAI,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAA8B,EAC9B,GAAoB;IAEpB,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,aAAa,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC;IACrC,IAAI,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,qBAAqB,EAAE,CAAC;YAC/C,IAAI,SAAS,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACjC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;QACjD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,QAA8B,EAC9B,IAAS;IAET,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,QAA8B,EAC9B,GAAyB;IAEzB,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,iBAAiB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,GAAyB;IACvD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"scope-filter.js","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAa;IAC3C,KAAK,EAAE,QAAQ;IACf,aAAa,EAAE,CAAC,GAAG,CAAC;IACpB,qBAAqB,EAAE,EAAE;IACzB,sBAAsB,EAAE,EAAE;CAC3B,CAAC;AAEF,SAAS,cAAc,CAAC,QAAkB;IACxC,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,SAAS,CACjB,2EAA2E;YACzE,0EAA0E,CAC7E,CAAC;IACJ,CAAC;AACH,CAAC;AAWD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAa;IACzC,cAAc,CAAC,GAAG,CAAC,CAAC;IACpB,IAAI,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAkB,EAClB,GAAoB;IAEpB,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzB,IAAI,aAAa,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC;IACrC,IAAI,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,qBAAqB,EAAE,CAAC;YAC/C,IAAI,SAAS,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACjC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;QACjD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAkB,EAClB,IAAS;IAET,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAkB,EAClB,GAAyB;IAEzB,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzB,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,iBAAiB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,GAAa;IAC3C,cAAc,CAAC,GAAG,CAAC,CAAC;IACpB,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC"}
@@ -12,12 +12,8 @@
12
12
  * - getEffectiveTenantId(ctx, args) — multi-tenant isolation guard
13
13
  * - decodeUnverifiedBearer(token) — DECODE-ONLY helper, NOT authentication.
14
14
  * The decoded payload is attacker-controlled. Production code MUST use a
15
- * signed JWT or opaque-token lookup (planned: 0.3.0) before treating the
16
- * payload as a trust boundary.
17
- *
18
- * Task: k177hejb4tc5em5p70m9hxwn3x88kyp4
19
- * Mission: k57b4t2q VR Cloud MVP Day 100
20
- * Orchestrator: Sigma — VantagePeers | 2026-06-13
15
+ * signed JWT or opaque-token lookup before treating the payload as a
16
+ * trust boundary.
21
17
  */
22
18
  import { z } from "zod";
23
19
  export declare const workspaceRoleSchema: z.ZodEnum<{
@@ -110,8 +106,8 @@ export declare function getEffectiveTenantId(ctx: TenantContext, args: {
110
106
  * middleware (the production trust boundary)
111
107
  *
112
108
  * For the production trust boundary, callers MUST substitute a signed JWT
113
- * (planned export in 0.3.0) or an opaque-token lookup against a Convex
114
- * tenancy table.
109
+ * verifier (not yet exported by this package) or an opaque-token lookup
110
+ * against a Convex tenancy table.
115
111
  *
116
112
  * Renamed from `resolveBearer` (0.2.0 pre-release) to make the lack of
117
113
  * verification impossible to miss at the call-site.
@@ -1 +1 @@
1
- {"version":3,"file":"tenancy-domain.d.ts","sourceRoot":"","sources":["../src/tenancy-domain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,mBAAmB;;;;EAAwC,CAAC;AACzE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAMhE,eAAO,MAAM,eAAe;;;;;iBAK1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAMxD,eAAO,MAAM,qBAAqB;;;;;;;;;iBAKhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAMpE,eAAO,MAAM,mBAAmB;;;;;;;;iBAI9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAMhE,MAAM,WAAW,qBAAqB;IACpC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;IAC3C,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;gBAE5B,OAAO,EAAE,qBAAqB;CAS3C;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAC5B,MAAM,CASR;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,QAAA,MAAM,mBAAmB;;;;;;;;iBAIvB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,CAAC,CAmBxB"}
1
+ {"version":3,"file":"tenancy-domain.d.ts","sourceRoot":"","sources":["../src/tenancy-domain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,mBAAmB;;;;EAAwC,CAAC;AACzE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAMhE,eAAO,MAAM,eAAe;;;;;iBAK1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAMxD,eAAO,MAAM,qBAAqB;;;;;;;;;iBAKhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAMpE,eAAO,MAAM,mBAAmB;;;;;;;;iBAI9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAMhE,MAAM,WAAW,qBAAqB;IACpC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;IAC3C,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;gBAE5B,OAAO,EAAE,qBAAqB;CAS3C;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAC5B,MAAM,CASR;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,QAAA,MAAM,mBAAmB;;;;;;;;iBAIvB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,CAAC,CAmBxB"}
@@ -12,12 +12,8 @@
12
12
  * - getEffectiveTenantId(ctx, args) — multi-tenant isolation guard
13
13
  * - decodeUnverifiedBearer(token) — DECODE-ONLY helper, NOT authentication.
14
14
  * The decoded payload is attacker-controlled. Production code MUST use a
15
- * signed JWT or opaque-token lookup (planned: 0.3.0) before treating the
16
- * payload as a trust boundary.
17
- *
18
- * Task: k177hejb4tc5em5p70m9hxwn3x88kyp4
19
- * Mission: k57b4t2q VR Cloud MVP Day 100
20
- * Orchestrator: Sigma — VantagePeers | 2026-06-13
15
+ * signed JWT or opaque-token lookup before treating the payload as a
16
+ * trust boundary.
21
17
  */
22
18
  import { z } from "zod";
23
19
  // ---------------------------------------------------------------------------
@@ -120,8 +116,8 @@ export function getEffectiveTenantId(ctx, args) {
120
116
  * middleware (the production trust boundary)
121
117
  *
122
118
  * For the production trust boundary, callers MUST substitute a signed JWT
123
- * (planned export in 0.3.0) or an opaque-token lookup against a Convex
124
- * tenancy table.
119
+ * verifier (not yet exported by this package) or an opaque-token lookup
120
+ * against a Convex tenancy table.
125
121
  *
126
122
  * Renamed from `resolveBearer` (0.2.0 pre-release) to make the lack of
127
123
  * verification impossible to miss at the call-site.
@@ -1 +1 @@
1
- {"version":3,"file":"tenancy-domain.js","sourceRoot":"","sources":["../src/tenancy-domain.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tenancy-domain.js","sourceRoot":"","sources":["../src/tenancy-domain.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantageos/cloud-identity",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Framework-agnostic identity + scope-filter primitives for VantagePeers Cloud (multi-tenant MCP).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,7 +53,7 @@
53
53
  "identity"
54
54
  ],
55
55
  "author": "VantagePeers Cloud",
56
- "license": "MIT",
56
+ "license": "FSL-1.1-Apache-2.0",
57
57
  "repository": {
58
58
  "type": "git",
59
59
  "url": "git+https://github.com/vantageos-agency/cloud-identity.git"