powerbin 0.1.0 → 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.
package/README.md CHANGED
@@ -46,9 +46,14 @@ for invalid arguments or a malformed link.
46
46
 
47
47
  ## One-time behavior
48
48
 
49
- Running `reveal` consumes the link. There is no confirmation prompt. Missing or
50
- malformed keys are rejected before contacting the server. A validly encoded but
51
- incorrect key can still consume the message before decryption fails.
49
+ Running `reveal` checks compatibility without consuming the link, then claims it
50
+ once. There is no confirmation prompt. Version 0.1.1 and later require the server
51
+ to report `protocol_version: 2` in its availability response. Older servers and
52
+ legacy v1 links are rejected before the consuming request. Update the PowerBin
53
+ server and browser app together, reload the browser, and create a fresh link.
54
+
55
+ Missing or malformed keys are rejected before contacting the server. A validly
56
+ encoded but incorrect key can still consume the message before decryption fails.
52
57
 
53
58
  The CLI never retries or follows redirects for the consuming request. If the
54
59
  response is lost after the server claims the message, it cannot be recovered.
package/dist/client.js CHANGED
@@ -91,11 +91,47 @@ async function responseBody(response) {
91
91
  }
92
92
  return new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(chunks));
93
93
  }
94
- /** A single consuming request. Loading crypto and validating the URL happen first. */
94
+ async function checkProtocol(link, timeoutMs) {
95
+ let response;
96
+ try {
97
+ response = await fetch(link.endpoint.slice(0, -'/reveal'.length), {
98
+ method: 'GET',
99
+ headers: { Accept: 'application/json' },
100
+ redirect: 'manual',
101
+ signal: AbortSignal.timeout(timeoutMs)
102
+ });
103
+ }
104
+ catch {
105
+ throw new CliError('The link availability check was lost or timed out. This request did not consume the message.');
106
+ }
107
+ if (response.status >= 300 && response.status < 400) {
108
+ await response.body?.cancel().catch(() => { });
109
+ throw new CliError('The service redirected the availability check. No redirect was followed. This request did not consume the message.');
110
+ }
111
+ if (!response.ok) {
112
+ await response.body?.cancel().catch(() => { });
113
+ const reason = [400, 401, 403, 404, 410, 429].includes(response.status)
114
+ ? statusError(response.status).message
115
+ : 'The server could not check this link.';
116
+ throw new CliError(`${reason} This request did not consume the message.`);
117
+ }
118
+ let metadata;
119
+ try {
120
+ metadata = JSON.parse(await responseBody(response));
121
+ }
122
+ catch {
123
+ throw new CliError('The link metadata response was incomplete or invalid. This request did not consume the message.');
124
+ }
125
+ if (!metadata || typeof metadata !== 'object' || !('protocol_version' in metadata) || metadata.protocol_version !== 2) {
126
+ throw new CliError('This link/server uses an unsupported encryption format. Update PowerBin server/browser, then create a new link. This request did not consume the message.');
127
+ }
128
+ }
129
+ /** Check compatibility without consuming, then make a single consuming request. */
95
130
  export async function revealLink(input, options = {}) {
96
131
  const link = parseLink(input);
97
132
  try {
98
133
  const crypto = loadCrypto();
134
+ await checkProtocol(link, options.timeoutMs ?? 15_000);
99
135
  let response;
100
136
  try {
101
137
  response = await fetch(link.endpoint, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powerbin",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Reveal and burn PowerBin one-time messages from your terminal, with local decryption.",
5
5
  "license": "CC0-1.0",
6
6
  "type": "module",