borgmcp-shared 0.4.0 → 0.4.2
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/README.md +27 -15
- package/SECURITY.md +6 -5
- package/dist/conformance/adapter.d.ts +58 -8
- package/dist/conformance/adapter.d.ts.map +1 -1
- package/dist/conformance/adapter.js +259 -14
- package/dist/conformance/adapter.js.map +1 -1
- package/dist/protocol/contract.d.ts +14 -1
- package/dist/protocol/contract.d.ts.map +1 -1
- package/dist/protocol/contract.js +14 -1
- package/dist/protocol/contract.js.map +1 -1
- package/dist/protocol/coordination.d.ts +25 -0
- package/dist/protocol/coordination.d.ts.map +1 -1
- package/dist/protocol/coordination.js +44 -0
- package/dist/protocol/coordination.js.map +1 -1
- package/dist/templates.d.ts +1 -1
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +9 -11
- package/dist/templates.js.map +1 -1
- package/docs/enrollment.md +18 -15
- package/docs/releasing.md +57 -35
- package/package.json +2 -2
- package/src/conformance/adapter.ts +588 -26
- package/src/protocol/contract.ts +14 -1
- package/src/protocol/coordination.ts +82 -0
- package/src/templates.ts +9 -11
package/src/protocol/contract.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ErrorCode } from './errors.js';
|
|
|
2
2
|
import { PROTOCOL_VERSION, type ProtocolVersion } from './version.js';
|
|
3
3
|
|
|
4
4
|
export const SHARED_PACKAGE_NAME = 'borgmcp-shared' as const;
|
|
5
|
-
export const SHARED_PACKAGE_VERSION = '0.4.
|
|
5
|
+
export const SHARED_PACKAGE_VERSION = '0.4.2' as const;
|
|
6
6
|
|
|
7
7
|
export const HEALTH_PATH = '/healthz' as const;
|
|
8
8
|
export const PROTOCOL_INFO_PATH = '/api/protocol' as const;
|
|
@@ -14,9 +14,22 @@ export const PROTOCOL_HTTP_CONTRACT = {
|
|
|
14
14
|
protocol: { method: 'GET', path: PROTOCOL_INFO_PATH, authenticated: false, success_status: 200 },
|
|
15
15
|
enrollment: { method: 'POST', path: ENROLLMENT_EXCHANGE_PATH, authenticated: 'invitation', success_status: 201 },
|
|
16
16
|
cubes: { method: 'POST', path: CUBES_PATH, authenticated: true, success_status: 201 },
|
|
17
|
+
drone_reassign: {
|
|
18
|
+
method: 'PATCH',
|
|
19
|
+
path: '/api/cubes/:cubeId/drones/:droneId',
|
|
20
|
+
authenticated: true,
|
|
21
|
+
success_status: 200,
|
|
22
|
+
},
|
|
23
|
+
drone_evict: {
|
|
24
|
+
method: 'DELETE',
|
|
25
|
+
path: '/api/cubes/:cubeId/drones/:droneId',
|
|
26
|
+
authenticated: true,
|
|
27
|
+
success_status: 200,
|
|
28
|
+
},
|
|
17
29
|
auth_missing_status: 401,
|
|
18
30
|
auth_invalid_status: 401,
|
|
19
31
|
cursor_expired_status: 410,
|
|
32
|
+
drone_evicted_status: 410,
|
|
20
33
|
content_too_large_status: 413,
|
|
21
34
|
unsupported_protocol_status: 426,
|
|
22
35
|
redirect_policy: 'error',
|
|
@@ -38,6 +38,28 @@ export interface ReadLogResult {
|
|
|
38
38
|
claims: ClaimRecord[];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export interface ReassignDroneRequest {
|
|
42
|
+
role_id: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ManagedDrone {
|
|
46
|
+
id: string;
|
|
47
|
+
cube_id: string;
|
|
48
|
+
role_id: string;
|
|
49
|
+
label: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ReassignDroneResult {
|
|
53
|
+
drone: ManagedDrone;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type EvictDroneRequest = Record<string, never>;
|
|
57
|
+
|
|
58
|
+
export interface EvictDroneResult {
|
|
59
|
+
drone_id: string;
|
|
60
|
+
evicted: true;
|
|
61
|
+
}
|
|
62
|
+
|
|
41
63
|
function object(value: unknown): Record<string, unknown> {
|
|
42
64
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
43
65
|
throw new ProtocolContractError('Expected a coordination object.');
|
|
@@ -84,6 +106,66 @@ function positiveInteger(value: unknown, name: string, maximum: number): number
|
|
|
84
106
|
return decoded;
|
|
85
107
|
}
|
|
86
108
|
|
|
109
|
+
function decodeManagedDrone(value: unknown): ManagedDrone {
|
|
110
|
+
const input = object(value);
|
|
111
|
+
exact(input, ['id', 'cube_id', 'role_id', 'label'], ['id', 'cube_id', 'role_id', 'label']);
|
|
112
|
+
return {
|
|
113
|
+
id: decodeUuid(input.id, ['id']),
|
|
114
|
+
cube_id: decodeUuid(input.cube_id, ['cube_id']),
|
|
115
|
+
role_id: decodeUuid(input.role_id, ['role_id']),
|
|
116
|
+
label: boundedString(input.label, 'label', 120),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function decodeReassignDroneRequest(value: unknown): ReassignDroneRequest {
|
|
121
|
+
const input = object(value);
|
|
122
|
+
exact(input, ['role_id'], ['role_id']);
|
|
123
|
+
return { role_id: decodeUuid(input.role_id, ['role_id']) };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function decodeReassignDroneRequestEnvelope(
|
|
127
|
+
value: unknown,
|
|
128
|
+
): ProtocolEnvelope<ReassignDroneRequest> {
|
|
129
|
+
return decodeProtocolEnvelope(value, decodeReassignDroneRequest);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function decodeReassignDroneResult(value: unknown): ReassignDroneResult {
|
|
133
|
+
const input = object(value);
|
|
134
|
+
exact(input, ['drone'], ['drone']);
|
|
135
|
+
return { drone: decodeManagedDrone(input.drone) };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function decodeReassignDroneResultEnvelope(
|
|
139
|
+
value: unknown,
|
|
140
|
+
): ProtocolEnvelope<ReassignDroneResult> {
|
|
141
|
+
return decodeProtocolEnvelope(value, decodeReassignDroneResult);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function decodeEvictDroneRequest(value: unknown): EvictDroneRequest {
|
|
145
|
+
const input = object(value);
|
|
146
|
+
exact(input, [], []);
|
|
147
|
+
return {};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function decodeEvictDroneRequestEnvelope(
|
|
151
|
+
value: unknown,
|
|
152
|
+
): ProtocolEnvelope<EvictDroneRequest> {
|
|
153
|
+
return decodeProtocolEnvelope(value, decodeEvictDroneRequest);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function decodeEvictDroneResult(value: unknown): EvictDroneResult {
|
|
157
|
+
const input = object(value);
|
|
158
|
+
exact(input, ['drone_id', 'evicted'], ['drone_id', 'evicted']);
|
|
159
|
+
if (input.evicted !== true) throw new ProtocolContractError('Invalid drone eviction result.');
|
|
160
|
+
return { drone_id: decodeUuid(input.drone_id, ['drone_id']), evicted: true };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function decodeEvictDroneResultEnvelope(
|
|
164
|
+
value: unknown,
|
|
165
|
+
): ProtocolEnvelope<EvictDroneResult> {
|
|
166
|
+
return decodeProtocolEnvelope(value, decodeEvictDroneResult);
|
|
167
|
+
}
|
|
168
|
+
|
|
87
169
|
export function decodeReadLogRequest(value: unknown): ReadLogRequest {
|
|
88
170
|
const input = object(value);
|
|
89
171
|
exact(input, ['cursor', 'limit'], ['cursor']);
|
package/src/templates.ts
CHANGED
|
@@ -242,9 +242,9 @@ The cube's release-cycle discipline has three documented shapes; the seat-holder
|
|
|
242
242
|
**Frontend/web-UI testing-track dispatch instruction:** for PRs touching user-facing web UI bundles, explicitly instruct Release Quality in the dispatch: "load the built page in a browser, capture console output, and include it in RQ-APPROVED [testing]." Diff-only review routinely misses client-side bundle errors.
|
|
243
243
|
|
|
244
244
|
**SR-exclusion list (autonomous-mode shape NOT eligible — explicit SR gate required regardless):**
|
|
245
|
-
- PRs introducing new auth-bypass call sites (
|
|
246
|
-
- PRs changing
|
|
247
|
-
- PRs modifying
|
|
245
|
+
- PRs introducing new auth-bypass call sites (scoped-store gates, admin-mode helpers)
|
|
246
|
+
- PRs changing authorization-decision caching mechanisms (session cache storage swaps)
|
|
247
|
+
- PRs modifying identity or session-token handling (verification, renewal, revocation)
|
|
248
248
|
- PRs touching CORS allowlist matching, encryption key handling, or webhook signature verification
|
|
249
249
|
|
|
250
250
|
These exclusions reflect the cube's documented threat model. Override requires explicit Queen authorization with the override condition documented in the merge trailer.
|
|
@@ -538,8 +538,7 @@ const SOFTWARE_DEV: Template = {
|
|
|
538
538
|
'PD-APPROVED',
|
|
539
539
|
'PS-APPROVED',
|
|
540
540
|
],
|
|
541
|
-
routing: '
|
|
542
|
-
default_to: ['coordinator', 'queen'],
|
|
541
|
+
routing: 'broadcast',
|
|
543
542
|
lifecycle: 'completion',
|
|
544
543
|
},
|
|
545
544
|
{
|
|
@@ -671,7 +670,7 @@ Workflow:
|
|
|
671
670
|
- Verify correctness: does the code do what the commit message claims? Tests pass? Bundle size acceptable? Follows project conventions?
|
|
672
671
|
- **Verify implementation quality + suggest refactors when appropriate.** Beyond "does it work," ask: is the code clean and readable? Specific things to call out — duplicated logic that could share a helper, dense or clever code that hides intent, unclear naming, missing abstractions for repeated non-trivial patterns, complex conditionals that would flatten, magic numbers, overly long functions, dead code, inconsistent in-file style. **Balance against "don't over-engineer"**: per the project's standing rule, three similar lines is better than a premature abstraction. Refactors should reduce real complexity, not add layers for hypothetical future cases. Refactor suggestions are typically NIT-class — post as \`REVIEW-FEEDBACK (nit: suggested refactor): <branch> <observation>\` and DO NOT block \`REVIEW-APPROVED\` on them; block only on patterns that compound technical debt or harm readability materially. Bigger refactors needing their own scope → file as a deferred-work issue rather than expanding the PR.
|
|
673
672
|
- **Replaced-module behavioral diff.** If the PR deletes file X and introduces file Y (or replaces a module's role wholesale), explicitly enumerate "behaviors X had — present in Y?" before approval. Spec-only review misses invariants the deleted module had realized but the spec didn't surface. The canonical reason for the discipline: prior cutovers have lost load-bearing filters exactly this way (the new module faithfully implemented the spec; the deleted module had silently realized an invariant the spec didn't name). Checking the introduced module against the deleted one directly catches it pre-merge.
|
|
674
|
-
- **Security review is Security Auditor's lane, not yours.** If the PR touches auth,
|
|
673
|
+
- **Security review is Security Auditor's lane, not yours.** If the PR touches auth, scoped data access (scoped-store wrappers / session-bound query helpers), encryption, secret handling, input validation, origin allowlists, rate limits, credential flows, or sensitive-data paths, surface that to the cube (e.g., note in your \`REVIEW-FEEDBACK\` or \`REVIEW-APPROVED\` post) so Security Auditor picks it up for parallel review. Coordinator holds the merge until both \`REVIEW-APPROVED\` AND \`SECURITY-APPROVED\` for security-touching PRs. You may still flag obvious security regressions you happen to spot, but you are not the dedicated security-review gate.
|
|
675
674
|
- For each finding worth flagging, post \`REVIEW-FEEDBACK: <branch> <observation>\` — high-confidence issues only. Sort blockers from nits explicitly.
|
|
676
675
|
- When done, post either \`REVIEW-APPROVED: <branch>\` (clean) or expect the Builder to address feedback and re-post \`REVIEW-READY:\`. Unaddressed refactor-NITs ride alongside \`REVIEW-APPROVED\` — they don't gate merge; the Coordinator merges on REVIEW-APPROVED regardless.
|
|
677
676
|
|
|
@@ -751,12 +750,12 @@ Boundaries:
|
|
|
751
750
|
detailed_description: `You are the cube's security specialist — the dedicated owner of the security expectations the project documents but no other role enforces. Other drones check correctness, behavior, experience, and performance; you check exploitability. Autonomous — coordinate through the log.
|
|
752
751
|
|
|
753
752
|
Your job:
|
|
754
|
-
- Review security-touching code changes for vulnerability classes: OWASP top 10, command injection, XSS, SQL injection, auth bypass, data leaks, path traversal, SSRF,
|
|
755
|
-
- Audit security-critical surfaces:
|
|
753
|
+
- Review security-touching code changes for vulnerability classes: OWASP top 10, command injection, XSS, SQL injection, auth bypass, data leaks, path traversal, SSRF, and races in authorization or session state.
|
|
754
|
+
- Audit security-critical surfaces: bearer and session verification, scoped data access (any function that gates data by session identity — scoped-store wrappers, session-bound query helpers, equivalent boundary guards), encryption (algorithms, key handling, IV/nonce uniqueness, secret storage), input validation at API boundaries (Zod schemas or equivalent), origin allowlists, rate limiters, dependency hygiene (CVE checks on dependency bumps), and sensitive local data paths.
|
|
756
755
|
- Run periodic full-codebase sweeps separate from per-pull-request review — walk the documented security expectations (project security instructions, threat-model docs, security checklists) and verify they still hold. Cadence: once per minor release or every ~2 weeks, whichever comes first. Catches the "we documented it but stopped enforcing it" failure mode.
|
|
757
756
|
|
|
758
757
|
When you engage on a PR:
|
|
759
|
-
- On regen, scan the log for \`REVIEW-READY:\` signals on branches touching security surface (auth,
|
|
758
|
+
- On regen, scan the log for \`REVIEW-READY:\` signals on branches touching security surface (auth, scoped data access, encryption, input validation, origin allowlists, rate limits, credential flows, sensitive-data paths, dependency bumps).
|
|
760
759
|
- For non-security-relevant changes (experience copy, version bumps, test infrastructure, internal refactors of non-security code), DON'T gate. Code Reviewer alone is the merge gate for those.
|
|
761
760
|
- Post \`STARTING: security review of <branch>\` and pull the diff.
|
|
762
761
|
|
|
@@ -812,8 +811,7 @@ const STARTER: Template = {
|
|
|
812
811
|
{
|
|
813
812
|
class: 'completion-gate',
|
|
814
813
|
prefixes: ['APPROVED'],
|
|
815
|
-
routing: '
|
|
816
|
-
default_to: ['coordinator', 'queen'],
|
|
814
|
+
routing: 'broadcast',
|
|
817
815
|
lifecycle: 'completion',
|
|
818
816
|
},
|
|
819
817
|
{
|