borgmcp 2.0.11 → 2.1.1

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.
Files changed (58) hide show
  1. package/README.md +11 -3
  2. package/dist/assimilate-cmd.d.ts +22 -4
  3. package/dist/assimilate-cmd.d.ts.map +1 -1
  4. package/dist/assimilate-cmd.js +109 -110
  5. package/dist/assimilate-cmd.js.map +1 -1
  6. package/dist/assimilate-deps.d.ts +7 -1
  7. package/dist/assimilate-deps.d.ts.map +1 -1
  8. package/dist/assimilate-deps.js +69 -25
  9. package/dist/assimilate-deps.js.map +1 -1
  10. package/dist/claude.d.ts +3 -1
  11. package/dist/claude.d.ts.map +1 -1
  12. package/dist/claude.js +26 -12
  13. package/dist/claude.js.map +1 -1
  14. package/dist/cli-help.d.ts +2 -0
  15. package/dist/cli-help.d.ts.map +1 -1
  16. package/dist/cli-help.js +25 -7
  17. package/dist/cli-help.js.map +1 -1
  18. package/dist/config.d.ts +7 -4
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +21 -4
  21. package/dist/config.js.map +1 -1
  22. package/dist/remote-client.js +1 -1
  23. package/dist/remote-client.js.map +1 -1
  24. package/dist/repository-cube-init.d.ts +72 -0
  25. package/dist/repository-cube-init.d.ts.map +1 -0
  26. package/dist/repository-cube-init.js +341 -0
  27. package/dist/repository-cube-init.js.map +1 -0
  28. package/dist/repository-identity.d.ts +29 -0
  29. package/dist/repository-identity.d.ts.map +1 -0
  30. package/dist/repository-identity.js +143 -0
  31. package/dist/repository-identity.js.map +1 -0
  32. package/dist/server-errors.d.ts +17 -0
  33. package/dist/server-errors.d.ts.map +1 -1
  34. package/dist/server-errors.js +32 -0
  35. package/dist/server-errors.js.map +1 -1
  36. package/dist/server-facade.d.ts +11 -1
  37. package/dist/server-facade.d.ts.map +1 -1
  38. package/dist/server-facade.js +33 -3
  39. package/dist/server-facade.js.map +1 -1
  40. package/dist/server-handshake.d.ts +38 -4
  41. package/dist/server-handshake.d.ts.map +1 -1
  42. package/dist/server-handshake.js +175 -13
  43. package/dist/server-handshake.js.map +1 -1
  44. package/docs/EXTRACTION_PROVENANCE.md +9 -7
  45. package/docs/LOCAL_SERVER.md +44 -9
  46. package/docs/RELEASING.md +21 -4
  47. package/package.json +2 -2
  48. package/src/assimilate-cmd.ts +179 -137
  49. package/src/assimilate-deps.ts +96 -24
  50. package/src/claude.ts +34 -14
  51. package/src/cli-help.ts +28 -7
  52. package/src/config.ts +30 -8
  53. package/src/remote-client.ts +1 -1
  54. package/src/repository-cube-init.ts +452 -0
  55. package/src/repository-identity.ts +199 -0
  56. package/src/server-errors.ts +41 -0
  57. package/src/server-facade.ts +47 -2
  58. package/src/server-handshake.ts +258 -14
@@ -1,12 +1,14 @@
1
1
  import { spawn as spawnChild, type SpawnOptions } from 'node:child_process';
2
2
  import { constants } from 'node:os';
3
- import { serverHelpText } from './cli-help.js';
3
+ import { cubeInitHelpText, isHelpFlag, serverHelpText } from './cli-help.js';
4
4
 
5
5
  export const SERVER_LIFECYCLE_COMMANDS = ['setup', 'start', 'stop', 'status', 'update', 'invite'] as const;
6
6
  export type ServerLifecycleCommand = typeof SERVER_LIFECYCLE_COMMANDS[number];
7
7
 
8
8
  export type ParsedServerFacadeArgs =
9
9
  | { kind: 'help' }
10
+ | { kind: 'cube-init-help' }
11
+ | { kind: 'cube-init'; args: string[] }
10
12
  | { kind: 'command'; command: ServerLifecycleCommand; args: string[] }
11
13
  | { kind: 'error'; reason: 'unknown-command'; command: string };
12
14
 
@@ -15,6 +17,12 @@ export function parseServerFacadeArgs(args: readonly string[]): ParsedServerFaca
15
17
  if (command === undefined || command === '--help' || command === '-h') {
16
18
  return { kind: 'help' };
17
19
  }
20
+ if (command === 'cube' && rest[0] === 'init') {
21
+ const args = rest.slice(1);
22
+ return args.some(isHelpFlag)
23
+ ? { kind: 'cube-init-help' }
24
+ : { kind: 'cube-init', args };
25
+ }
18
26
  if (!(SERVER_LIFECYCLE_COMMANDS as readonly string[]).includes(command)) {
19
27
  return { kind: 'error', reason: 'unknown-command', command };
20
28
  }
@@ -49,6 +57,12 @@ export interface ServerFacadeOutputDeps {
49
57
  writeStderr(text: string): void;
50
58
  }
51
59
 
60
+ export interface ServerFacadeClientDeps {
61
+ cubeInit(args: readonly string[]): Promise<number>;
62
+ }
63
+
64
+ export type AssimilateDepsBuilder = typeof import('./assimilate-deps.js').buildDefaultAssimilateDeps;
65
+
52
66
  export type ServerFacadeProcessResult =
53
67
  | { kind: 'exited'; code: number }
54
68
  | { kind: 'signaled'; signal: NodeJS.Signals }
@@ -65,6 +79,31 @@ const defaultOutputDeps: ServerFacadeOutputDeps = {
65
79
  writeStderr: (text) => process.stderr.write(text),
66
80
  };
67
81
 
82
+ export function buildDefaultServerFacadeClientDeps(
83
+ buildDeps?: AssimilateDepsBuilder,
84
+ ): ServerFacadeClientDeps {
85
+ return {
86
+ cubeInit: async (args) => {
87
+ const [{ parseAssimilateArgs }, { buildDefaultAssimilateDeps }, { runAssimilate }] = await Promise.all([
88
+ import('./parse-assimilate-args.js'),
89
+ import('./assimilate-deps.js'),
90
+ import('./assimilate-cmd.js'),
91
+ ]);
92
+ const parsed = parseAssimilateArgs([...args]);
93
+ if (!parsed.ok || parsed.role !== undefined) {
94
+ process.stderr.write(`${parsed.ok ? 'borg server cube init does not accept a role' : parsed.error}\n`);
95
+ return 1;
96
+ }
97
+ return runAssimilate(
98
+ { role: undefined, flags: parsed.flags, mode: 'cube-init' },
99
+ (buildDeps ?? buildDefaultAssimilateDeps)(),
100
+ );
101
+ },
102
+ };
103
+ }
104
+
105
+ const defaultClientDeps = buildDefaultServerFacadeClientDeps();
106
+
68
107
  const MAX_RENDERED_COMMAND_CODE_POINTS = 80;
69
108
 
70
109
  function inertCommand(command: string): string {
@@ -86,7 +125,7 @@ function inertCommand(command: string): string {
86
125
  export function unknownServerCommandText(command: string): string {
87
126
  return (
88
127
  `Unknown server command: ${inertCommand(command)}.\n` +
89
- `Available commands: setup, start, stop, status, update, invite.\n` +
128
+ `Available commands: setup, start, stop, status, update, invite, cube init.\n` +
90
129
  `Next: run borg server --help.\n`
91
130
  );
92
131
  }
@@ -168,6 +207,7 @@ export async function runEarlyServerFacade(
168
207
  argv: readonly string[],
169
208
  deps: ServerFacadeProcessDeps = defaultProcessDeps,
170
209
  output: ServerFacadeOutputDeps = defaultOutputDeps,
210
+ client: ServerFacadeClientDeps = defaultClientDeps,
171
211
  ): Promise<number | null> {
172
212
  if (argv[2] !== 'server') return null;
173
213
  const parsed = parseServerFacadeArgs(argv.slice(3));
@@ -175,10 +215,15 @@ export async function runEarlyServerFacade(
175
215
  output.writeStdout(serverHelpText());
176
216
  return 0;
177
217
  }
218
+ if (parsed.kind === 'cube-init-help') {
219
+ output.writeStdout(cubeInitHelpText());
220
+ return 0;
221
+ }
178
222
  if (parsed.kind === 'error') {
179
223
  output.writeStderr(unknownServerCommandText(parsed.command));
180
224
  return 1;
181
225
  }
226
+ if (parsed.kind === 'cube-init') return client.cubeInit(parsed.args);
182
227
 
183
228
  const result = await runServerFacadeProcess(parsed, deps);
184
229
  if (result.kind === 'spawn-error') {
@@ -4,8 +4,12 @@ import {
4
4
  ENROLLMENT_EXCHANGE_PATH,
5
5
  HEALTH_PATH,
6
6
  PROTOCOL_INFO_PATH,
7
+ REPOSITORY_CUBE_ASSOCIATION_PATH,
8
+ REPOSITORY_CUBE_RESOLVE_PATH,
7
9
  createAttachRequestEnvelope,
8
10
  createProtocolEnvelope,
11
+ decodeAssociateRepositoryCubeRequest,
12
+ decodeAssociateRepositoryCubeResponseEnvelope,
9
13
  decodeAttachResponseEnvelope,
10
14
  decodeCreateCubeRequest,
11
15
  decodeCreateCubeResponseEnvelope,
@@ -13,10 +17,16 @@ import {
13
17
  decodeEnrollmentExchangeResponseEnvelope,
14
18
  decodeProtocolErrorEnvelope,
15
19
  decodeProtocolTagPreflight,
20
+ decodeResolveRepositoryCubeRequest,
21
+ decodeResolveRepositoryCubeResponseEnvelope,
16
22
  ErrorCode,
23
+ type AssociateRepositoryCubeResponse,
24
+ type CreateCubeRepository,
17
25
  type CreateCubeResponse,
26
+ type CubeTemplate,
18
27
  type DroneRuntimeMetadata,
19
28
  type ProtocolTagPreflight,
29
+ type ResolveRepositoryCubeResponse,
20
30
  type ServerCapability,
21
31
  } from 'borgmcp-shared/protocol';
22
32
  import { createHash, randomUUID } from 'node:crypto';
@@ -45,6 +55,11 @@ import {
45
55
  BorgServerError,
46
56
  BorgServerTrustError,
47
57
  BorgServerUnreachableError,
58
+ CubeCreationConfirmationError,
59
+ CubeCreationOutcomeUnknownError,
60
+ RepositoryAssociationOperationError,
61
+ RepositoryAssociationOutcomeUnknownError,
62
+ RepositoryAssociationResolutionError,
48
63
  } from './server-errors.js';
49
64
  import { DroneEvictedError, DRONE_EVICTED_CODE } from './drone-lifecycle.js';
50
65
  import { readBoundedResponseBody } from './server-response.js';
@@ -181,7 +196,7 @@ export interface ServerAttachResult {
181
196
  }
182
197
 
183
198
  /**
184
- * Attach an enrolled client principal to one granted cube/role over protocol v3.
199
+ * Attach an enrolled client principal to one granted cube/role over protocol v5.
185
200
  * The client CSPRNG-generates the session bearer and persists it PENDING in the
186
201
  * OS keychain (keyed by the stable per-seat identity) BEFORE this request, so an
187
202
  * interrupted/lost response is recovered by re-sending the exact same bearer —
@@ -554,6 +569,210 @@ export async function resumeBorgServerEnrollment(
554
569
  });
555
570
  }
556
571
 
572
+ async function requireMatchingServerCredential(
573
+ origin: string,
574
+ trustIdentity: string,
575
+ parentCredential: string,
576
+ loadCredentialRecord: typeof getServerCredentialRecord,
577
+ ): Promise<void> {
578
+ const active = await loadCredentialRecord(origin, trustIdentity);
579
+ if (!active || active.credential !== parentCredential || !active.clientId) {
580
+ throw new BorgServerError('CREDENTIAL_REJECTED', 'stored Borg server credential was rejected');
581
+ }
582
+ }
583
+
584
+ async function protocolErrorCode(response: Response): Promise<ErrorCode | undefined> {
585
+ try {
586
+ return decodeProtocolErrorEnvelope(
587
+ JSON.parse(await readHandshakeBodyWithTimeout(response)),
588
+ ).error.code;
589
+ } catch {
590
+ return undefined;
591
+ }
592
+ }
593
+
594
+ /** Resolve one client-scoped repository association without mutation. */
595
+ export async function resolveBorgServerRepositoryCube(
596
+ origin: string,
597
+ trustIdentity: string,
598
+ parentCredential: string,
599
+ input: { workingRepoName: string; repository: CreateCubeRepository },
600
+ deps: {
601
+ fetchImpl?: FetchLike;
602
+ loadCredentialRecord?: typeof getServerCredentialRecord;
603
+ } = {},
604
+ ): Promise<ResolveRepositoryCubeResponse> {
605
+ await requireMatchingServerCredential(
606
+ origin,
607
+ trustIdentity,
608
+ parentCredential,
609
+ deps.loadCredentialRecord ?? getServerCredentialRecord,
610
+ );
611
+ const request = decodeResolveRepositoryCubeRequest({
612
+ working_repo_name: input.workingRepoName,
613
+ repository: input.repository,
614
+ });
615
+ const controller = new AbortController();
616
+ const timeout = setTimeout(() => controller.abort(), HANDSHAKE_TIMEOUT_MS);
617
+ let response: Response;
618
+ try {
619
+ response = await (deps.fetchImpl ?? fetch)(handshakeUrl(origin, REPOSITORY_CUBE_RESOLVE_PATH), {
620
+ method: 'POST',
621
+ redirect: 'error',
622
+ signal: controller.signal,
623
+ headers: {
624
+ Accept: 'application/json',
625
+ 'Content-Type': 'application/json',
626
+ Authorization: `Bearer ${parentCredential}`,
627
+ },
628
+ body: JSON.stringify(createProtocolEnvelope(randomUUID(), request)),
629
+ });
630
+ } catch (error) {
631
+ throw new RepositoryAssociationResolutionError();
632
+ } finally {
633
+ clearTimeout(timeout);
634
+ }
635
+ if (response.status === 401 || response.status === 403) {
636
+ throw new BorgServerError('CREDENTIAL_REJECTED', 'Borg server enrollment was rejected');
637
+ }
638
+ if (response.status !== 200) {
639
+ throw new RepositoryAssociationResolutionError();
640
+ }
641
+ try {
642
+ const decoded = decodeResolveRepositoryCubeResponseEnvelope(
643
+ JSON.parse(await readHandshakeBodyWithTimeout(response)),
644
+ ).payload;
645
+ if (
646
+ decoded.result === 'resolved' &&
647
+ (decoded.repository.kind !== request.repository.kind ||
648
+ decoded.repository.value !== request.repository.value ||
649
+ decoded.working_repo_name !== request.working_repo_name)
650
+ ) {
651
+ throw new Error('association mismatch');
652
+ }
653
+ return decoded;
654
+ } catch {
655
+ throw new RepositoryAssociationResolutionError();
656
+ }
657
+ }
658
+
659
+ export async function resolveLocalBorgServerRepositoryCube(
660
+ origin: string,
661
+ trustIdentity: string,
662
+ parentCredential: string,
663
+ input: { workingRepoName: string; repository: CreateCubeRepository },
664
+ deps: { loadTrust?: typeof loadBorgServerTrust } = {},
665
+ ): Promise<ResolveRepositoryCubeResponse> {
666
+ const trust = await (deps.loadTrust ?? loadBorgServerTrust)(origin);
667
+ if (trust.identity !== trustIdentity) {
668
+ throw new BorgServerTrustError('Borg server trust identity changed; refusing repository resolution');
669
+ }
670
+ return resolveBorgServerRepositoryCube(origin, trustIdentity, parentCredential, input, {
671
+ fetchImpl: trust.fetchImpl,
672
+ });
673
+ }
674
+
675
+ /** Atomically associate an explicit accessible cube after local confirmation. */
676
+ export async function associateBorgServerRepositoryCube(
677
+ origin: string,
678
+ trustIdentity: string,
679
+ parentCredential: string,
680
+ input: { cubeId: string; workingRepoName: string; repository: CreateCubeRepository },
681
+ deps: {
682
+ fetchImpl?: FetchLike;
683
+ loadCredentialRecord?: typeof getServerCredentialRecord;
684
+ } = {},
685
+ ): Promise<AssociateRepositoryCubeResponse> {
686
+ await requireMatchingServerCredential(
687
+ origin,
688
+ trustIdentity,
689
+ parentCredential,
690
+ deps.loadCredentialRecord ?? getServerCredentialRecord,
691
+ );
692
+ const request = decodeAssociateRepositoryCubeRequest({
693
+ cube_id: input.cubeId,
694
+ working_repo_name: input.workingRepoName,
695
+ repository: input.repository,
696
+ });
697
+ const controller = new AbortController();
698
+ const timeout = setTimeout(() => controller.abort(), HANDSHAKE_TIMEOUT_MS);
699
+ let response: Response;
700
+ try {
701
+ response = await (deps.fetchImpl ?? fetch)(handshakeUrl(origin, REPOSITORY_CUBE_ASSOCIATION_PATH), {
702
+ method: 'PUT',
703
+ redirect: 'error',
704
+ signal: controller.signal,
705
+ headers: {
706
+ Accept: 'application/json',
707
+ 'Content-Type': 'application/json',
708
+ Authorization: `Bearer ${parentCredential}`,
709
+ },
710
+ body: JSON.stringify(createProtocolEnvelope(randomUUID(), request)),
711
+ });
712
+ } catch {
713
+ throw new RepositoryAssociationOutcomeUnknownError();
714
+ } finally {
715
+ clearTimeout(timeout);
716
+ }
717
+ if (response.status === 401) {
718
+ throw new BorgServerError('CREDENTIAL_REJECTED', 'Borg server enrollment was rejected');
719
+ }
720
+ if (response.status === 403) {
721
+ if (await protocolErrorCode(response) !== ErrorCode.ACCESS_DENIED) {
722
+ throw new RepositoryAssociationOutcomeUnknownError();
723
+ }
724
+ throw new RepositoryAssociationOperationError('access-denied');
725
+ }
726
+ if (response.status === 409) {
727
+ const code = await protocolErrorCode(response);
728
+ if (code === ErrorCode.REPOSITORY_ALREADY_ASSOCIATED) {
729
+ throw new RepositoryAssociationOperationError('repository-already-associated');
730
+ }
731
+ if (code === ErrorCode.CUBE_ALREADY_ASSOCIATED) {
732
+ throw new RepositoryAssociationOperationError('cube-already-associated');
733
+ }
734
+ if (code === ErrorCode.INVALID_INPUT) {
735
+ throw new RepositoryAssociationOperationError('invalid-cube');
736
+ }
737
+ throw new RepositoryAssociationOutcomeUnknownError();
738
+ }
739
+ if (response.status !== 200) {
740
+ throw new RepositoryAssociationOutcomeUnknownError();
741
+ }
742
+ try {
743
+ const decoded = decodeAssociateRepositoryCubeResponseEnvelope(
744
+ JSON.parse(await readHandshakeBodyWithTimeout(response)),
745
+ ).payload;
746
+ if (
747
+ decoded.cube_id !== request.cube_id ||
748
+ decoded.repository.kind !== request.repository.kind ||
749
+ decoded.repository.value !== request.repository.value ||
750
+ decoded.working_repo_name !== request.working_repo_name
751
+ ) {
752
+ throw new Error('association mismatch');
753
+ }
754
+ return decoded;
755
+ } catch (error) {
756
+ throw new RepositoryAssociationOutcomeUnknownError();
757
+ }
758
+ }
759
+
760
+ export async function associateLocalBorgServerRepositoryCube(
761
+ origin: string,
762
+ trustIdentity: string,
763
+ parentCredential: string,
764
+ input: { cubeId: string; workingRepoName: string; repository: CreateCubeRepository },
765
+ deps: { loadTrust?: typeof loadBorgServerTrust } = {},
766
+ ): Promise<AssociateRepositoryCubeResponse> {
767
+ const trust = await (deps.loadTrust ?? loadBorgServerTrust)(origin);
768
+ if (trust.identity !== trustIdentity) {
769
+ throw new BorgServerTrustError('Borg server trust identity changed; refusing repository association');
770
+ }
771
+ return associateBorgServerRepositoryCube(origin, trustIdentity, parentCredential, input, {
772
+ fetchImpl: trust.fetchImpl,
773
+ });
774
+ }
775
+
557
776
  /**
558
777
  * Create one repository cube through the narrow owner capability. The retry
559
778
  * key is persisted in the OS keychain before network I/O and reused exactly
@@ -564,7 +783,12 @@ export async function createBorgServerCube(
564
783
  origin: string,
565
784
  trustIdentity: string,
566
785
  parentCredential: string,
567
- input: { projectRoot: string; name: string },
786
+ input: {
787
+ name: string;
788
+ workingRepoName: string;
789
+ repository: CreateCubeRepository;
790
+ template: CubeTemplate;
791
+ },
568
792
  deps: {
569
793
  fetchImpl?: FetchLike;
570
794
  loadCredentialRecord?: typeof getServerCredentialRecord;
@@ -589,13 +813,16 @@ export async function createBorgServerCube(
589
813
  origin,
590
814
  trustIdentity,
591
815
  clientId: active.clientId,
592
- projectRoot: input.projectRoot,
593
816
  name: input.name,
594
- template: 'default',
817
+ workingRepoName: input.workingRepoName,
818
+ repository: input.repository,
819
+ template: input.template,
595
820
  });
596
821
  const request = decodeCreateCubeRequest({
597
822
  retry_key: pending.retryKey,
598
823
  name: pending.name,
824
+ working_repo_name: pending.workingRepoName,
825
+ repository: pending.repository,
599
826
  template: pending.template,
600
827
  });
601
828
 
@@ -623,7 +850,10 @@ export async function createBorgServerCube(
623
850
  clearTimeout(timeout);
624
851
  }
625
852
  }
626
- if (!response) throw lastTransportError;
853
+ if (!response) {
854
+ void lastTransportError;
855
+ throw new CubeCreationOutcomeUnknownError();
856
+ }
627
857
  if (response.status === 401 || response.status === 403) {
628
858
  throw new BorgServerError('CREDENTIAL_REJECTED', 'Borg server enrollment was rejected');
629
859
  }
@@ -634,23 +864,32 @@ export async function createBorgServerCube(
634
864
  );
635
865
  }
636
866
  if (response.status === 409) {
637
- throw new Error('Borg server cube creation retry state conflicted');
867
+ throw new CubeCreationConfirmationError('The Borg server rejected the repository cube operation identity.');
638
868
  }
639
869
  if (response.status !== 201) {
640
- throw new Error(`Borg server cube creation failed (HTTP ${response.status})`);
870
+ throw new CubeCreationOutcomeUnknownError();
641
871
  }
642
872
  let decoded: ReturnType<typeof decodeCreateCubeResponseEnvelope>;
643
873
  try {
644
874
  decoded = decodeCreateCubeResponseEnvelope(
645
875
  JSON.parse(await readHandshakeBodyWithTimeout(response)),
646
876
  );
647
- } catch (error) {
648
- if (error instanceof Error && error.message.includes('response limit')) throw error;
649
- throw new Error('Borg server returned an invalid cube creation envelope');
877
+ } catch {
878
+ throw new CubeCreationOutcomeUnknownError();
879
+ }
880
+ if (
881
+ decoded.payload.repository.kind !== pending.repository.kind ||
882
+ decoded.payload.repository.value !== pending.repository.value
883
+ ) {
884
+ throw new CubeCreationConfirmationError();
885
+ }
886
+ try {
887
+ await (deps.clearCubeCreation ?? clearPendingServerCubeCreation)(
888
+ pending as PendingServerCubeCreationRecord,
889
+ );
890
+ } catch {
891
+ throw new CubeCreationConfirmationError('The server confirmed the cube, but local retry state could not be finalized.');
650
892
  }
651
- await (deps.clearCubeCreation ?? clearPendingServerCubeCreation)(
652
- pending as PendingServerCubeCreationRecord,
653
- );
654
893
  return decoded.payload;
655
894
  }
656
895
 
@@ -658,7 +897,12 @@ export async function createLocalBorgServerCube(
658
897
  origin: string,
659
898
  trustIdentity: string,
660
899
  parentCredential: string,
661
- input: { projectRoot: string; name: string },
900
+ input: {
901
+ name: string;
902
+ workingRepoName: string;
903
+ repository: CreateCubeRepository;
904
+ template: CubeTemplate;
905
+ },
662
906
  deps: { loadTrust?: typeof loadBorgServerTrust } = {},
663
907
  ): Promise<CreateCubeResponse> {
664
908
  const trust = await (deps.loadTrust ?? loadBorgServerTrust)(origin);