@opengeni/vaults-protonpass 0.0.0-stage → 3.2.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/LICENSE ADDED
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ Copyright 2026 Cloudgeni-ai
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.
package/README.md CHANGED
@@ -1,3 +1,137 @@
1
- # Temporary Holding Version
1
+ # `@opengeni/vaults-protonpass`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The Proton Pass provider for Opengeni vaults. It implements `VaultProvider`
4
+ from `@opengeni/vaults` by running the official Proton Pass CLI
5
+ ([`pass-cli`](https://protonpass.github.io/pass-cli/)) 2.4.2 under a
6
+ personal access token (PAT) that the customer scopes to vaults or items.
7
+
8
+ Load it only with a dynamic `import()` from the API or the worker
9
+ (`test/import-guard.test.ts`).
10
+
11
+ ## The CLI is operator-installed
12
+
13
+ This package does not download, bundle or redistribute `pass-cli`. The
14
+ operator installs it on the API and worker hosts and points the adapter at it.
15
+ Before each session the adapter checks that:
16
+
17
+ - the binary is an absolute path (or `pass-cli` found on a fixed
18
+ `/usr/bin:/bin` PATH) and is not world-writable;
19
+ - `pass-cli --version` reports exactly `PROTON_PASS_CLI_VERSION` (2.4.2), or
20
+ `expectedVersion`;
21
+ - its SHA-256 matches `expectedSha256`, when given.
22
+
23
+ ## Usage
24
+
25
+ ```ts
26
+ const { createProtonPassVaultProvider } = await import("@opengeni/vaults-protonpass");
27
+ const provider = createProtonPassVaultProvider({
28
+ token, // "pst_…::…", decrypted from the connection's stored credential
29
+ binaryPath: "/opt/proton/pass-cli",
30
+ stateRoot: "/var/lib/opengeni/protonpass", // optional; default <tmpdir>/opengeni-protonpass
31
+ });
32
+ // … use it, then
33
+ await provider.close(); // logs out and wipes the session directory
34
+ ```
35
+
36
+ Other options: `expectedVersion`, `expectedSha256`, `commandTimeoutMs`
37
+ (default 60 s), `maxStdoutBytes` (32 MB), `sessionMaxAgeMs` (105 minutes),
38
+ `idleCloseMs` (15 minutes), `valueCacheMs` and `maxItemViewsPerList` (500).
39
+ `operations` narrows the provider to the operations verified at setup and
40
+ refuses the rest without running the CLI. `runner`, `hostEnv` and `now` exist
41
+ for tests.
42
+
43
+ Servers use `createProtonPassProviderPool({ binaryPath, … })` once per
44
+ process. `pool.provider(token, { connectionId?, operations? })` returns a
45
+ provider whose session is created on its first call. Sessions are scoped to
46
+ the connection id and token: one connection reuses its logged-in CLI session,
47
+ while different connections (even with the same token) and different tokens
48
+ never share a session, state directory, key or cache. Without a connection id
49
+ the provider gets a session of its own. A connection's new token replaces its
50
+ idle old session. Sessions unused for 15 minutes (`poolIdleMs`) are logged
51
+ out, wiped and forgotten. `maxSessions` (32) is a hard limit: the least
52
+ recently used idle session makes room, and when every session is busy the
53
+ call fails with `provider_rate_limited` (retry after
54
+ `PROTON_PASS_POOL_BUSY_RETRY_MS`, 5 seconds). `pool.close()` logs them all
55
+ out and refuses later calls.
56
+
57
+ ## How it runs the CLI
58
+
59
+ - **No shell, bounded output.** Every command is spawned with an argument
60
+ list, a deadline, bounded stdout and stderr, and a process-group kill. At
61
+ most `PROTON_PASS_MAX_PROCESSES` (4) CLI processes run at once per process,
62
+ and commands for one connection are serialized. A finished process hands
63
+ its slot straight to the next waiting command, so the limit holds under
64
+ contention.
65
+ - **Secrets never go on argv.** The PAT is passed only in the environment of
66
+ `pass-cli login`. Item values for create go in on stdin
67
+ (`item create … --from-template -`).
68
+ - **Private, isolated state.** Each provider has its own `0700` state
69
+ directory (used as `HOME` and `PROTON_PASS_SESSION_DIR`), the `env` key
70
+ provider with a random per-instance `PROTON_PASS_ENCRYPTION_KEY`, telemetry
71
+ and update checks off, and a minimal environment (only proxy and CA
72
+ settings pass through). The directory is removed on `close()`, idle close
73
+ and process exit.
74
+ - **Session lifecycle.** PAT sessions last about two hours, so the adapter
75
+ logs in again after 105 minutes. A "requires an authenticated client" reply
76
+ re-logs in and retries once. An invalidated session re-logs in and retries
77
+ reads only; a write whose outcome is unknown is never retried.
78
+ - **Agent tokens.** For an agent PAT the CLI requires a reason on every
79
+ command; the adapter sends a short one built from the action and, when the
80
+ caller supplies it, the requesting authority. Agent tokens cannot list
81
+ item values in bulk, so listing views each item (at most 500 per list, else
82
+ `provider_inventory_incomplete`); each view is an audit event in Proton.
83
+ Bulk listings view only this share's items that have files, under the same
84
+ bound.
85
+ - **Errors.** Failures map to `VaultProviderError` codes from the CLI's
86
+ `Reason:` lines and HTTP status. Raw stderr is never surfaced. A failed
87
+ write is definite only when it failed before sending (bad input, no
88
+ session, missing agent reason) or Proton refused that write itself: a
89
+ rejection status (400, 401, 403, 404, 409, 413, 422 or 429) or a known
90
+ rejection reason (permission, not found, stale revision, lock, quota,
91
+ invalid input). A 5xx, any other status, a server-failure or unrecognised
92
+ reason (`Timeout`, `InsertFailed`, …), a timeout or an error after the
93
+ request is `provider_outcome_unknown` and is never retried. The CLI prints
94
+ a decoded reason without its HTTP status, so a reason outside the
95
+ rejection list is treated as possibly applied. A failed login is
96
+ `provider_auth_invalid` only when Proton refused the token (400, 401, 403
97
+ or 422, or a permission or not-found reason, in the token-login context);
98
+ local, network, server and unrecognised failures are
99
+ `provider_unavailable` (429 is `provider_rate_limited`).
100
+
101
+ ## What it can do
102
+
103
+ `PROTON_PASS_OPERATIONS` declares exactly this:
104
+
105
+ | Operation | Supported | Notes |
106
+ | --- | --- | --- |
107
+ | List vaults | yes | `share list --only-vaults`; write access from the share role. Falls back to `vault list` (access unknown) when shares cannot be listed. |
108
+ | List items, read values | yes | Values read live, cached per attempt; `forceRefresh` bypasses the cache. |
109
+ | Read files | yes | Downloaded to a private directory, at most 256 KiB. |
110
+ | Create | login, note, custom (`api_key`) | Including a TOTP seed on logins. A login takes only fields named `username`, `email` and `password` plus one TOTP; any other field is refused before running anything (use an `api_key` item). |
111
+ | Update | **no** | The CLI's update takes values only as `--field` arguments and cannot change TOTP or time fields, so updates would put secrets on argv. Reported as `provider_unavailable` without running anything. |
112
+ | Archive | trash (recoverable) | `item trash`. Permanent delete and restore are not exposed. |
113
+ | Create vault, write files | no | |
114
+
115
+ Writes to a share whose role is read-only are refused locally; Proton
116
+ enforces the rest.
117
+
118
+ The per-attempt value cache belongs to one provider instance (one token and
119
+ session), so providers that share an attempt budget never see each other's
120
+ values. Items with more than 50 fields or 20 websites (or a website over
121
+ 2,048 characters) make the listing `provider_inventory_incomplete` instead of
122
+ being mirrored cut down.
123
+
124
+ Field ids: `@note`, `login.username`, `login.email`, `login.password`,
125
+ `login.totp`, `x:<name>` (extra fields), `s:<section>/<field>` (custom item
126
+ sections), `ssh.*`, `wifi.*`, `card.*`, `identity.*` and `@file:<id>`, with
127
+ `#n` on repeated names.
128
+
129
+ ## Testing
130
+
131
+ `bun test` runs against `test/fake-pass-cli.ts`, a fake CLI that follows the
132
+ 2.4.2 source's arguments, JSON output and error lines. It covers the shared
133
+ provider contract (read side), session lifecycle, agent reasons, error
134
+ mapping, timeouts, output bounds, permissions and isolation.
135
+
136
+ No live Proton account was used: the output shapes and error lines come from
137
+ the CLI source and documentation, not from a live run.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The shared vault contracts this adapter implements, re-exported for the
3
+ * package's modules so every file names one source.
4
+ */
5
+ export { VaultProviderError, type ProviderCallBudget, type ProviderCallOperation, type ProviderFieldMeta, type ProviderFieldValue, type ProviderItemMeta, type ProviderVault, type ProviderWriteField, type ProviderWriteItem, type VaultProvider, type VaultProviderAuthority, type VaultProviderCapabilities, type VaultProviderContext, type VaultProviderErrorCode, } from "@opengeni/vaults";
6
+ export { VAULT_LIMITS, parseTotpInput, type SecretMaterial, type TotpParams, type VaultFieldKind, type VaultItemType, type VaultProviderOperations, } from "@opengeni/contracts";
@@ -0,0 +1,57 @@
1
+ import { VaultProviderError } from "./contract.js";
2
+ import type { ProcessResult } from "./runner.js";
3
+ /** The session can no longer be used; a fresh login is required. */
4
+ export declare const SESSION_INVALIDATED: RegExp;
5
+ /** Pre-flight: the CLI found no usable session before contacting the API. */
6
+ export declare const NOT_AUTHENTICATED: RegExp;
7
+ /** The Proton API error code name in stderr, if any (a closed enum, safe to report). */
8
+ export declare function protonReason(stderr: string): string | null;
9
+ export declare function protonStatus(stderr: string): number | null;
10
+ /**
11
+ * Statuses with which Proton refuses a request before applying it: a bad or
12
+ * invalid request (400, 422), an unauthorised or forbidden one (401, 403), a
13
+ * missing target (404), a conflict (409), an oversized payload (413) or
14
+ * throttling (429). Any other status (500, 502, 503, 504 from Proton or a
15
+ * proxy, or anything unexpected) may follow an applied change.
16
+ */
17
+ export declare const PROTON_DEFINITE_REJECTION_STATUSES: ReadonlySet<number>;
18
+ /**
19
+ * `ProtonApiErrorCode` names (pass-cli 2.4.2 pass/src/error.rs) that describe
20
+ * a refusal of the request itself: permission, a missing or deleted target, a
21
+ * stale revision or key, a lock, a quota, or invalid input. Server-side
22
+ * failures that may follow a partial write (`Timeout`, `InsertFailed`,
23
+ * `SelectFailed`, `UploadFailure`, the `Provider*` family) and any name not
24
+ * listed here leave a mutation's outcome unknown.
25
+ */
26
+ export declare const PROTON_DEFINITE_REJECTION_REASONS: ReadonlySet<string>;
27
+ /**
28
+ * True when stderr shows Proton refused the request without applying it: a
29
+ * known rejection reason, or (when no reason was decoded) a rejection status.
30
+ */
31
+ export declare function isDefiniteRejection(stderr: string): boolean;
32
+ /**
33
+ * Maps a failed pass-cli run to a provider error. `subject` names the action in
34
+ * the message (for example "list items"); stderr content never leaves here
35
+ * except the closed Proton error-code name.
36
+ */
37
+ export declare function mapProtonFailure(result: ProcessResult, subject: string): VaultProviderError;
38
+ /**
39
+ * Whether a failed mutation is known not to have taken effect. Only a server
40
+ * rejection inside the mutation's own error context (`Error creating …`,
41
+ * `Error trashing item`) with a known rejection reason or status, or a
42
+ * pre-flight marker, counts. A server error (500, 502, 503, 504 or any other
43
+ * status or reason outside the rejection sets), a timeout, a signal, a
44
+ * cancelled call or an error after the request (for example the agent monitor
45
+ * event sent after a create) leaves the outcome unknown.
46
+ */
47
+ export declare function isDefiniteMutationFailure(result: ProcessResult, mutationContext: RegExp): boolean;
48
+ export declare function outcomeUnknown(subject: string): VaultProviderError;
49
+ /**
50
+ * Maps a failed `pass-cli login`. Only Proton's own refusal of the token
51
+ * (a 400, 401, 403 or 422 answer, or a permission or not-found reason, in
52
+ * the token-login context) asks the customer for a new token. Local
53
+ * failures (key provider, disk, an unexpected CLI error), network trouble,
54
+ * throttling and server errors are `provider_unavailable` or
55
+ * `provider_rate_limited`; reconnecting would not help with them.
56
+ */
57
+ export declare function loginFailure(result: ProcessResult): VaultProviderError;
@@ -0,0 +1,6 @@
1
+ export { PROTON_PASS_MAX_ITEM_VIEWS_PER_LIST, PROTON_PASS_OPERATIONS, PROTON_PASS_VALUE_CACHE_MS, createProtonPassVaultProvider, protonPassCapabilities, type ProtonPassVaultProvider, type ProtonPassVaultProviderOptions, } from "./provider.js";
2
+ export { PROTON_PASS_CLI_VERSION, PROTON_PASS_COMMAND_TIMEOUT_MS, PROTON_PASS_IDLE_CLOSE_MS, PROTON_PASS_MAX_PROCESSES, PROTON_PASS_SESSION_MAX_AGE_MS, type ProtonCliOptions, } from "./session.js";
3
+ export { FILE_FIELD_PREFIX, LOGIN_FIELD_IDS, NOTE_FIELD_ID } from "./mapping.js";
4
+ export { PROTON_PASS_POOL_BUSY_RETRY_MS, PROTON_PASS_POOL_IDLE_MS, PROTON_PASS_POOL_MAX_SESSIONS, createProtonPassProviderPool, type ProtonPassPoolProviderOptions, type ProtonPassProviderPool, type ProtonPassProviderPoolOptions, } from "./pool.js";
5
+ export { restrictToOperations } from "./operations.js";
6
+ export type { ProcessRequest, ProcessResult, ProcessRunner } from "./runner.js";