borgmcp-server 0.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 (71) hide show
  1. package/LICENSE +105 -0
  2. package/NOTICE +8 -0
  3. package/README.md +119 -0
  4. package/SECURITY.md +38 -0
  5. package/THIRD_PARTY_NOTICES.md +35 -0
  6. package/dist/bootstrap.d.ts +18 -0
  7. package/dist/bootstrap.js +104 -0
  8. package/dist/bootstrap.js.map +1 -0
  9. package/dist/cli.d.ts +7 -0
  10. package/dist/cli.js +96 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/coordination-api.d.ts +25 -0
  13. package/dist/coordination-api.js +457 -0
  14. package/dist/coordination-api.js.map +1 -0
  15. package/dist/credentials.d.ts +58 -0
  16. package/dist/credentials.js +244 -0
  17. package/dist/credentials.js.map +1 -0
  18. package/dist/enrollment.d.ts +17 -0
  19. package/dist/enrollment.js +122 -0
  20. package/dist/enrollment.js.map +1 -0
  21. package/dist/https-server.d.ts +94 -0
  22. package/dist/https-server.js +814 -0
  23. package/dist/https-server.js.map +1 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +2 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/main.d.ts +3 -0
  28. package/dist/main.js +67 -0
  29. package/dist/main.js.map +1 -0
  30. package/dist/migrations.d.ts +8 -0
  31. package/dist/migrations.js +366 -0
  32. package/dist/migrations.js.map +1 -0
  33. package/dist/network-policy.d.ts +13 -0
  34. package/dist/network-policy.js +49 -0
  35. package/dist/network-policy.js.map +1 -0
  36. package/dist/operator-error.d.ts +3 -0
  37. package/dist/operator-error.js +63 -0
  38. package/dist/operator-error.js.map +1 -0
  39. package/dist/principal.d.ts +32 -0
  40. package/dist/principal.js +64 -0
  41. package/dist/principal.js.map +1 -0
  42. package/dist/protocol-draft.d.ts +2 -0
  43. package/dist/protocol-draft.js +31 -0
  44. package/dist/protocol-draft.js.map +1 -0
  45. package/dist/service.d.ts +61 -0
  46. package/dist/service.js +455 -0
  47. package/dist/service.js.map +1 -0
  48. package/dist/start-options.d.ts +2 -0
  49. package/dist/start-options.js +46 -0
  50. package/dist/start-options.js.map +1 -0
  51. package/dist/store.d.ts +327 -0
  52. package/dist/store.js +1729 -0
  53. package/dist/store.js.map +1 -0
  54. package/npm-shrinkwrap.json +1942 -0
  55. package/package.json +60 -0
  56. package/src/bootstrap.ts +127 -0
  57. package/src/cli.ts +102 -0
  58. package/src/coordination-api.ts +508 -0
  59. package/src/credentials.ts +319 -0
  60. package/src/enrollment.ts +156 -0
  61. package/src/https-server.ts +962 -0
  62. package/src/index.ts +3 -0
  63. package/src/main.ts +73 -0
  64. package/src/migrations.ts +394 -0
  65. package/src/network-policy.ts +65 -0
  66. package/src/operator-error.ts +97 -0
  67. package/src/principal.ts +106 -0
  68. package/src/protocol-draft.ts +32 -0
  69. package/src/service.ts +525 -0
  70. package/src/start-options.ts +46 -0
  71. package/src/store.ts +2316 -0
package/LICENSE ADDED
@@ -0,0 +1,105 @@
1
+ # Functional Source License, Version 1.1, ALv2 Future License
2
+
3
+ ## Abbreviation
4
+
5
+ FSL-1.1-ALv2
6
+
7
+ ## Notice
8
+
9
+ Copyright 2026 Byte Ventures
10
+
11
+ ## Terms and Conditions
12
+
13
+ ### Licensor ("We")
14
+
15
+ The party offering the Software under these Terms and Conditions.
16
+
17
+ ### The Software
18
+
19
+ The "Software" is each version of the software that we make available under
20
+ these Terms and Conditions, as indicated by our inclusion of these Terms and
21
+ Conditions with the Software.
22
+
23
+ ### License Grant
24
+
25
+ Subject to your compliance with this License Grant and the Patents,
26
+ Redistribution and Trademark clauses below, we hereby grant you the right to
27
+ use, copy, modify, create derivative works, publicly perform, publicly display
28
+ and redistribute the Software for any Permitted Purpose identified below.
29
+
30
+ ### Permitted Purpose
31
+
32
+ A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
33
+ means making the Software available to others in a commercial product or
34
+ service that:
35
+
36
+ 1. substitutes for the Software;
37
+
38
+ 2. substitutes for any other product or service we offer using the Software
39
+ that exists as of the date we make the Software available; or
40
+
41
+ 3. offers the same or substantially similar functionality as the Software.
42
+
43
+ Permitted Purposes specifically include using the Software:
44
+
45
+ 1. for your internal use and access;
46
+
47
+ 2. for non-commercial education;
48
+
49
+ 3. for non-commercial research; and
50
+
51
+ 4. in connection with professional services that you provide to a licensee
52
+ using the Software in accordance with these Terms and Conditions.
53
+
54
+ ### Patents
55
+
56
+ To the extent your use for a Permitted Purpose would necessarily infringe our
57
+ patents, the license grant above includes a license under our patents. If you
58
+ make a claim against any party that the Software infringes or contributes to
59
+ the infringement of any patent, then your patent license to the Software ends
60
+ immediately.
61
+
62
+ ### Redistribution
63
+
64
+ The Terms and Conditions apply to all copies, modifications and derivatives of
65
+ the Software.
66
+
67
+ If you redistribute any copies, modifications or derivatives of the Software,
68
+ you must include a copy of or a link to these Terms and Conditions and not
69
+ remove any copyright notices provided in or with the Software.
70
+
71
+ ### Disclaimer
72
+
73
+ THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
74
+ IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
75
+ PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
76
+
77
+ IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
78
+ SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
79
+ EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
80
+
81
+ ### Trademarks
82
+
83
+ Except for displaying the License Details and identifying us as the origin of
84
+ the Software, you have no right under these Terms and Conditions to use our
85
+ trademarks, trade names, service marks or product names.
86
+
87
+ ## Grant of Future License
88
+
89
+ We hereby irrevocably grant you an additional license to use the Software under
90
+ the Apache License, Version 2.0 that is effective on the second anniversary of
91
+ the date we make the Software available. On or after that date, you may use the
92
+ Software under the Apache License, Version 2.0, in which case the following
93
+ will apply:
94
+
95
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
96
+ this file except in compliance with the License.
97
+
98
+ You may obtain a copy of the License at
99
+
100
+ http://www.apache.org/licenses/LICENSE-2.0
101
+
102
+ Unless required by applicable law or agreed to in writing, software distributed
103
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
104
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
105
+ specific language governing permissions and limitations under the License.
package/NOTICE ADDED
@@ -0,0 +1,8 @@
1
+ borgmcp-server
2
+ Copyright 2026 Byte Ventures
3
+
4
+ This product is licensed under the Functional Source License, Version 1.1,
5
+ ALv2 Future License (FSL-1.1-ALv2). See LICENSE for the controlling terms.
6
+
7
+ This product includes third-party software. See THIRD_PARTY_NOTICES.md and the
8
+ CycloneDX SBOM distributed with each release workflow artifact.
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # borgmcp-server
2
+
3
+ `borgmcp-server` is the self-hosted coordination authority for Borg MCP. It
4
+ stores cube state locally and serves the Borg protocol over authenticated
5
+ HTTPS.
6
+
7
+ ## Release status
8
+
9
+ The current source is the unpublished `0.1.1-preview` recovery release candidate.
10
+ Purpose-bound owner enrollment, idempotent multi-cube creation, and stable
11
+ prior-seat reattachment have completed coordinated client/server security
12
+ review and process dogfood against the audited exact `borgmcp-shared@0.3.0`
13
+ registry release.
14
+
15
+ Setup prepares local identity and storage and prints one-time recovery and
16
+ owner-enrollment secrets; it creates no cube. The package remains unreleased
17
+ until the exact release commit and tagged artifact complete the documented
18
+ review, authorization, and protected-publication gates.
19
+
20
+ ## Requirements
21
+
22
+ - Node.js 22.12 or later
23
+ - npm 10 or later
24
+ - A private local data directory with sufficient disk space
25
+
26
+ ## Install after publication
27
+
28
+ The global package command becomes available only after the pending npm release
29
+ completes. Until then, source and preview reviewers should follow the release
30
+ status above and the [release runbook](docs/releasing.md), not expect this
31
+ registry install to succeed.
32
+
33
+ ```sh
34
+ npm install --global borgmcp-server
35
+ ```
36
+
37
+ ## Set up a local server
38
+
39
+ The default data directory is `~/.borg/server`. Setup creates the local
40
+ database, credential-digest key, local certificate authority, server
41
+ certificate, one recovery credential, and one enrollment invitation. It creates
42
+ no cube. Run it in a private terminal because both secrets are printed only
43
+ once.
44
+
45
+ ```sh
46
+ borg-mcp-server setup
47
+ borg-mcp-server start
48
+ ```
49
+
50
+ The server listens on `https://127.0.0.1:7091` by default. Use
51
+ `BORG_SERVER_DATA_DIR` to select another data directory.
52
+
53
+ ## Network configuration
54
+
55
+ Loopback is the safe default. Binding to a private LAN address requires both an
56
+ explicit address and `--lan` consent:
57
+
58
+ ```sh
59
+ borg-mcp-server start --host 192.168.1.20 --port 7091 --lan
60
+ ```
61
+
62
+ Before LAN startup, move `ca.key` out of the runtime data directory. Keep the
63
+ CA private key offline; the running service does not need it. Public, wildcard,
64
+ unspecified, multicast, and otherwise unsafe bind addresses are rejected.
65
+
66
+ TLS files may instead be supplied explicitly with
67
+ `BORG_SERVER_TLS_KEY_FILE`, `BORG_SERVER_TLS_CERT_FILE`, and
68
+ `BORG_SERVER_TLS_CA_FILE`. Run `borg-mcp-server help` for the complete command
69
+ summary.
70
+
71
+ ## Offline credential administration
72
+
73
+ Stop the server before all offline client administration:
74
+
75
+ ```sh
76
+ borg-mcp-server client-rotate <client-id>
77
+ borg-mcp-server client-revoke <client-id>
78
+ borg-mcp-server client-invite
79
+ borg-mcp-server owner-invite
80
+ borg-mcp-server client-grant <client-id> <cube-id> <read|write|manage>
81
+ borg-mcp-server client-ungrant <client-id> <cube-id>
82
+ ```
83
+
84
+ Invitation commands read the recovery credential from a private hidden terminal
85
+ prompt, never argv or environment. Rotation and invitation commands print their
86
+ replacement secret once. Treat setup, enrollment, invitation, and rotation
87
+ output as secrets; do not paste it into issues, logs, or chat.
88
+
89
+ ## Capacity controls
90
+
91
+ The server accepts positive integer values for these optional environment
92
+ variables:
93
+
94
+ - `BORG_SERVER_MAX_ACTIVITY_ENTRIES_PER_CUBE`
95
+ - `BORG_SERVER_MAX_DATABASE_BYTES`
96
+ - `BORG_SERVER_MIN_FREE_DISK_BYTES`
97
+
98
+ Invalid values fail closed before the server starts.
99
+ Cube creation is additionally bounded to 100 cubes per creating client and
100
+ 1,000 cubes per server. Exact idempotent retries do not consume quota twice.
101
+
102
+ ## Library entry point
103
+
104
+ The package exports `runCli`, `CliIo`, and `ServerService` for controlled
105
+ embedding. Most installations should use the `borg-mcp-server` executable.
106
+
107
+ ## Security and support
108
+
109
+ Read [SECURITY.md](SECURITY.md) before exposing the service beyond loopback.
110
+ Use GitHub Issues for reproducible non-sensitive defects. Report
111
+ vulnerabilities privately as described in the security policy.
112
+
113
+ ## License
114
+
115
+ This server is licensed under the Functional Source License, Version 1.1,
116
+ ALv2 Future License (`FSL-1.1-ALv2`). Each released version becomes available
117
+ under Apache License 2.0 on the second anniversary of the date that version was
118
+ made available. See [LICENSE](LICENSE) for the controlling terms and
119
+ [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for dependency disclosures.
package/SECURITY.md ADDED
@@ -0,0 +1,38 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes are provided for the latest release of `borgmcp-server`. Upgrade
6
+ to the newest published version before reporting a problem that may already be
7
+ resolved.
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ Use GitHub's private vulnerability reporting for this repository:
12
+
13
+ https://github.com/Byte-Ventures/borg-mcp-server/security/advisories/new
14
+
15
+ Do not open a public issue for a suspected vulnerability. Include the affected
16
+ version, impact, prerequisites, and minimal reproduction steps. Remove all real
17
+ credentials, invitation tokens, private keys, database contents, and private
18
+ deployment details from the report.
19
+
20
+ We will acknowledge the report through the private advisory and coordinate
21
+ validation, remediation, and disclosure there. Please do not disclose the
22
+ issue publicly until a release or other mitigation is available and disclosure
23
+ has been coordinated.
24
+
25
+ ## Deployment guidance
26
+
27
+ - Keep the default loopback binding unless private-LAN access is necessary.
28
+ - Treat `--lan` as explicit consent to network exposure, not as a firewall or
29
+ access-control substitute.
30
+ - Keep the local CA private key offline whenever the server is running on a
31
+ private LAN.
32
+ - Restrict the data directory and all generated credentials to the service
33
+ account.
34
+ - Capture one-time setup and rotation credentials only in a private terminal.
35
+ - Stop the server before offline credential rotation or revocation.
36
+ - Back up sensitive state using encrypted storage and test restoration in a
37
+ separate environment.
38
+ - Review dependency and release provenance before upgrading.
@@ -0,0 +1,35 @@
1
+ # Third-party notices
2
+
3
+ `borgmcp-server` installs the following production dependency tree from the
4
+ public npm registry. Copyright remains with the respective authors. The
5
+ license identifiers below are taken from the exact locked package metadata;
6
+ refer to each installed package for its complete license text.
7
+
8
+ | Package | Version | License |
9
+ | --- | --- | --- |
10
+ | `@noble/hashes` | 1.4.0 | MIT |
11
+ | `@peculiar/asn1-cms` | 2.8.0 | MIT |
12
+ | `@peculiar/asn1-csr` | 2.8.0 | MIT |
13
+ | `@peculiar/asn1-ecc` | 2.8.0 | MIT |
14
+ | `@peculiar/asn1-pfx` | 2.8.0 | MIT |
15
+ | `@peculiar/asn1-pkcs8` | 2.8.0 | MIT |
16
+ | `@peculiar/asn1-pkcs9` | 2.8.0 | MIT |
17
+ | `@peculiar/asn1-rsa` | 2.8.0 | MIT |
18
+ | `@peculiar/asn1-schema` | 2.8.0 | MIT |
19
+ | `@peculiar/asn1-x509` | 2.8.0 | MIT |
20
+ | `@peculiar/asn1-x509-attr` | 2.8.0 | MIT |
21
+ | `@peculiar/utils` | 2.0.3 | MIT |
22
+ | `@peculiar/x509` | 1.14.3 | MIT |
23
+ | `asn1js` | 3.0.10 | BSD-3-Clause |
24
+ | `bytestreamjs` | 2.0.1 | BSD-3-Clause |
25
+ | `pkijs` | 3.4.0 | BSD-3-Clause |
26
+ | `pvtsutils` | 1.3.6 | MIT |
27
+ | `pvutils` | 1.1.5 | MIT |
28
+ | `reflect-metadata` | 0.2.2 | Apache-2.0 |
29
+ | `selfsigned` | 5.5.0 | MIT |
30
+ | `tslib` | 1.14.1, 2.8.1 | 0BSD |
31
+ | `tsyringe` | 4.10.0 | MIT |
32
+
33
+ The release workflow generates a CycloneDX SBOM from `npm-shrinkwrap.json` for
34
+ the exact artifact under review. Development-only packages are recorded in the
35
+ source shrinkwrap and generated SBOM but are not installed for package users.
@@ -0,0 +1,18 @@
1
+ export interface BootstrapResult {
2
+ readonly serverId: string;
3
+ readonly caFingerprint: string;
4
+ readonly recoveryCredential: string;
5
+ readonly initialInvitation: string;
6
+ readonly paths: {
7
+ readonly database: string;
8
+ readonly digestKey: string;
9
+ readonly caKey: string;
10
+ readonly caCertificate: string;
11
+ readonly serverKey: string;
12
+ readonly serverCertificate: string;
13
+ readonly config: string;
14
+ };
15
+ }
16
+ export declare function bootstrapServer(dataDirectory: string, bindHost?: string, clock?: () => Date): Promise<BootstrapResult>;
17
+ export declare function loadDigestKey(path: string): Promise<Buffer>;
18
+ export declare function loadTlsPrivateKey(path: string): Promise<Buffer>;
@@ -0,0 +1,104 @@
1
+ import { createHash, randomBytes, randomUUID, X509Certificate } from "node:crypto";
2
+ import { lstat, readFile, stat, writeFile } from "node:fs/promises";
3
+ import { join, resolve } from "node:path";
4
+ import { generate } from "selfsigned";
5
+ import { CredentialAuthority, CredentialDigester } from "./credentials.js";
6
+ import { openStore } from "./store.js";
7
+ export async function bootstrapServer(dataDirectory, bindHost = "127.0.0.1", clock = () => new Date()) {
8
+ const directory = resolve(dataDirectory);
9
+ const paths = {
10
+ database: join(directory, "borg.db"),
11
+ digestKey: join(directory, "credential-digest.key"),
12
+ caKey: join(directory, "ca.key"),
13
+ caCertificate: join(directory, "ca.crt"),
14
+ serverKey: join(directory, "server.key"),
15
+ serverCertificate: join(directory, "server.crt"),
16
+ config: join(directory, "server.json"),
17
+ };
18
+ const ca = await generate([{ name: "commonName", value: "Borg Local CA" }], {
19
+ algorithm: "sha256",
20
+ keyType: "ec",
21
+ extensions: [
22
+ { name: "basicConstraints", cA: true, pathLenConstraint: 0, critical: true },
23
+ { name: "keyUsage", keyCertSign: true, cRLSign: true, critical: true },
24
+ ],
25
+ });
26
+ const server = await generate([{ name: "commonName", value: "Borg Local Server" }], {
27
+ algorithm: "sha256",
28
+ keyType: "ec",
29
+ ca: { key: ca.private, cert: ca.cert },
30
+ extensions: [
31
+ { name: "basicConstraints", cA: false, critical: true },
32
+ { name: "keyUsage", digitalSignature: true, keyAgreement: true, critical: true },
33
+ { name: "extKeyUsage", serverAuth: true },
34
+ { name: "subjectAltName", altNames: [{ type: 7, ip: bindHost }] },
35
+ ],
36
+ });
37
+ const serverId = randomUUID();
38
+ const caCertificate = new X509Certificate(ca.cert);
39
+ const caFingerprint = createHash("sha256")
40
+ .update(caCertificate.publicKey.export({ type: "spki", format: "der" }))
41
+ .digest("hex");
42
+ const digestKey = randomBytes(32);
43
+ const runtime = await openStore({ path: paths.database, clock });
44
+ try {
45
+ await Promise.all([
46
+ writePrivate(paths.digestKey, digestKey),
47
+ writePrivate(paths.caKey, ca.private),
48
+ writePrivate(paths.caCertificate, ca.cert),
49
+ writePrivate(paths.serverKey, server.private),
50
+ writePrivate(paths.serverCertificate, server.cert),
51
+ writePrivate(paths.config, JSON.stringify({
52
+ server_id: serverId,
53
+ ca_spki_sha256: caFingerprint,
54
+ bind_host: bindHost,
55
+ }, null, 2)),
56
+ ]);
57
+ const digester = new CredentialDigester(digestKey);
58
+ digestKey.fill(0);
59
+ try {
60
+ const authority = new CredentialAuthority(runtime.credentials, digester, clock);
61
+ const recoveryCredential = authority.createRecoveryCredential();
62
+ const initialInvitation = authority.createBootstrapInvitation(15 * 60_000);
63
+ return {
64
+ serverId,
65
+ caFingerprint,
66
+ recoveryCredential,
67
+ initialInvitation,
68
+ paths,
69
+ };
70
+ }
71
+ finally {
72
+ digester.destroy();
73
+ }
74
+ }
75
+ finally {
76
+ digestKey.fill(0);
77
+ runtime.close();
78
+ }
79
+ }
80
+ async function writePrivate(path, value) {
81
+ await writeFile(path, value, { flag: "wx", mode: 0o600 });
82
+ if (((await stat(path)).mode & 0o777) !== 0o600) {
83
+ throw new Error("Bootstrap file permissions are not private.");
84
+ }
85
+ }
86
+ export async function loadDigestKey(path) {
87
+ const key = await loadPrivateFile(path, "Credential digest key");
88
+ if (key.length !== 32) {
89
+ key.fill(0);
90
+ throw new Error("Credential digest key is invalid or not private.");
91
+ }
92
+ return key;
93
+ }
94
+ export async function loadTlsPrivateKey(path) {
95
+ return loadPrivateFile(path, "TLS private key");
96
+ }
97
+ async function loadPrivateFile(path, label) {
98
+ const metadata = await lstat(path);
99
+ if (!metadata.isFile() || (metadata.mode & 0o077) !== 0) {
100
+ throw new Error(`${label} is invalid or not private.`);
101
+ }
102
+ return readFile(path);
103
+ }
104
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAkBvC,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,aAAqB,EACrB,QAAQ,GAAG,WAAW,EACtB,KAAK,GAAe,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;IAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG;QACZ,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC;QACpC,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,uBAAuB,CAAC;QACnD,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC;QAChC,aAAa,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC;QACxC,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC;QACxC,iBAAiB,EAAE,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC;QAChD,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC;KACvC,CAAC;IACF,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE;QAC1E,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,IAAI;QACb,UAAU,EAAE;YACV,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC5E,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;SACvE;KACF,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,EAAE;QAClF,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,IAAI;QACb,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE;QACtC,UAAU,EAAE;YACV,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE;YACvD,EAAE,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;YAChF,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,IAAI,EAAE;YACzC,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE;SAClE;KACF,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,MAAM,aAAa,GAAG,IAAI,eAAe,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,aAAa,GAAG,UAAU,CAAC,QAAQ,CAAC;SACvC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;SACvE,MAAM,CAAC,KAAK,CAAC,CAAC;IACjB,MAAM,SAAS,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IACjE,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,YAAY,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC;YACxC,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC;YACrC,YAAY,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC,IAAI,CAAC;YAC1C,YAAY,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC;YAC7C,YAAY,CAAC,KAAK,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC;YAClD,YAAY,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC;gBACxC,SAAS,EAAE,QAAQ;gBACnB,cAAc,EAAE,aAAa;gBAC7B,SAAS,EAAE,QAAQ;aACpB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;SACb,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACnD,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,mBAAmB,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;YAChF,MAAM,kBAAkB,GAAG,SAAS,CAAC,wBAAwB,EAAE,CAAC;YAChE,MAAM,iBAAiB,GAAG,SAAS,CAAC,yBAAyB,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;YAC3E,OAAO;gBACL,QAAQ;gBACR,aAAa;gBACb,kBAAkB;gBAClB,iBAAiB;gBACjB,KAAK;aACN,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,KAAsB;IAC9D,MAAM,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1D,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY;IAC9C,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;IACjE,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACtB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAClD,OAAO,eAAe,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;AAClD,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,KAAa;IACxD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,6BAA6B,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { ServerService } from "./service.js";
2
+ export interface CliIo {
3
+ readonly stdout: (message: string) => void;
4
+ readonly stderr: (message: string) => void;
5
+ readonly readSecret?: (prompt: string) => Promise<string>;
6
+ }
7
+ export declare function runCli(args: readonly string[], service: ServerService, io: CliIo): Promise<number>;
package/dist/cli.js ADDED
@@ -0,0 +1,96 @@
1
+ const usage = `Usage: borg-mcp-server <command> [options]
2
+
3
+ Commands:
4
+ setup Prepare an offline server installation
5
+ start Start the server process
6
+ client-rotate <client-id> Rotate one client credential offline
7
+ client-revoke <client-id> Revoke one client and its credentials offline
8
+ client-grant <client-id> <cube-id> <read|write|manage> Set one offline cube grant
9
+ client-ungrant <client-id> <cube-id> Remove one offline cube grant
10
+ client-invite Create one ordinary client invitation using a private recovery prompt
11
+ owner-invite Replace the unclaimed owner invitation using a private recovery prompt
12
+ help Show this help
13
+
14
+ Start options:
15
+ --host <ip> Explicit bind address (default: 127.0.0.1)
16
+ --port <number> Listen port (default: 7091)
17
+ --lan Consent to this start on a private LAN address
18
+
19
+ TLS files:
20
+ BORG_SERVER_DATA_DIR (default: ~/.borg/server), or explicit
21
+ BORG_SERVER_TLS_KEY_FILE, BORG_SERVER_TLS_CERT_FILE, and BORG_SERVER_TLS_CA_FILE
22
+
23
+ Stop the server before running offline client administration commands.`;
24
+ export async function runCli(args, service, io) {
25
+ const [command, ...extraArgs] = args;
26
+ switch (command) {
27
+ case "setup":
28
+ if (extraArgs.length !== 0)
29
+ return invalidArguments(io);
30
+ if (service.setup === undefined) {
31
+ io.stderr("Server setup is unavailable.");
32
+ return 1;
33
+ }
34
+ const result = await service.setup();
35
+ io.stdout(`Server setup complete.\nRecovery credential (shown once): ${result.recoveryCredential}\nInitial enrollment invitation (shown once): ${result.initialInvitation}`);
36
+ return 0;
37
+ case "start":
38
+ await service.start(extraArgs);
39
+ return 0;
40
+ case "client-rotate":
41
+ if (extraArgs.length !== 1 || service.rotateClient === undefined)
42
+ return invalidArguments(io);
43
+ io.stdout(`Client credential rotated (shown once): ${await service.rotateClient(extraArgs[0])}`);
44
+ return 0;
45
+ case "client-revoke":
46
+ if (extraArgs.length !== 1 || service.revokeClient === undefined)
47
+ return invalidArguments(io);
48
+ await service.revokeClient(extraArgs[0]);
49
+ io.stdout("Client revoked.");
50
+ return 0;
51
+ case "client-grant": {
52
+ if (extraArgs.length !== 3 || service.grantClient === undefined)
53
+ return invalidArguments(io);
54
+ const access = extraArgs[2];
55
+ if (access !== "read" && access !== "write" && access !== "manage")
56
+ return invalidArguments(io);
57
+ await service.grantClient(extraArgs[0], extraArgs[1], access);
58
+ io.stdout("Client cube grant updated.");
59
+ return 0;
60
+ }
61
+ case "client-ungrant":
62
+ if (extraArgs.length !== 2 || service.ungrantClient === undefined)
63
+ return invalidArguments(io);
64
+ await service.ungrantClient(extraArgs[0], extraArgs[1]);
65
+ io.stdout("Client cube grant removed.");
66
+ return 0;
67
+ case "client-invite":
68
+ case "owner-invite": {
69
+ if (extraArgs.length !== 0 || io.readSecret === undefined)
70
+ return invalidArguments(io);
71
+ const operation = command === "client-invite"
72
+ ? service.createClientInvitation
73
+ : service.replaceOwnerInvitation;
74
+ if (operation === undefined)
75
+ return invalidArguments(io);
76
+ const recovery = await io.readSecret("Recovery credential: ");
77
+ const invitation = await operation(recovery);
78
+ io.stdout(`Enrollment invitation (shown once): ${invitation}`);
79
+ return 0;
80
+ }
81
+ case "help":
82
+ case "--help":
83
+ case "-h":
84
+ case undefined:
85
+ io.stdout(usage);
86
+ return 0;
87
+ default:
88
+ io.stderr("Unknown command.");
89
+ return 1;
90
+ }
91
+ }
92
+ function invalidArguments(io) {
93
+ io.stderr("Invalid command arguments.");
94
+ return 1;
95
+ }
96
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Principal } from "./principal.js";
2
+ import type { CredentialAuthority } from "./credentials.js";
3
+ import { type StoreRuntime } from "./store.js";
4
+ export interface CoordinationRequest {
5
+ readonly method: string;
6
+ readonly path: string;
7
+ readonly principal: Principal;
8
+ readonly body?: unknown;
9
+ readonly cursor?: string;
10
+ readonly signal: AbortSignal;
11
+ }
12
+ export interface CoordinationResponse {
13
+ readonly status: number;
14
+ readonly body?: unknown;
15
+ readonly stream?: AsyncIterable<string>;
16
+ }
17
+ export declare class CoordinationApi {
18
+ #private;
19
+ constructor(runtime: StoreRuntime, authority: CredentialAuthority);
20
+ armReplayTransition(): {
21
+ readonly reached: Promise<void>;
22
+ readonly release: () => void;
23
+ };
24
+ handle(request: CoordinationRequest): Promise<CoordinationResponse>;
25
+ }