@buoy-gg/license 7.0.44 → 7.0.46

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 (69) hide show
  1. package/lib/commonjs/LicenseManager.js +2 -2
  2. package/lib/commonjs/config.js +2 -2
  3. package/lib/commonjs/devToken.js +6 -0
  4. package/lib/commonjs/headless.js +1 -1
  5. package/lib/commonjs/hooks.js +2 -2
  6. package/lib/commonjs/index.js +2 -2
  7. package/lib/commonjs/index.web.js +2 -2
  8. package/lib/commonjs/nodeSession.js +6 -0
  9. package/lib/commonjs/serviceKey.js +6 -0
  10. package/lib/commonjs/sessionManager.js +6 -0
  11. package/lib/commonjs/sessionToken.js +6 -0
  12. package/lib/commonjs/syncCredential.js +6 -0
  13. package/lib/module/LicenseManager.js +1 -1
  14. package/lib/module/config.js +1 -1
  15. package/lib/module/devToken.js +6 -0
  16. package/lib/module/headless.js +1 -1
  17. package/lib/module/hooks.js +1 -1
  18. package/lib/module/index.js +1 -1
  19. package/lib/module/index.web.js +1 -1
  20. package/lib/module/nodeSession.js +6 -0
  21. package/lib/module/serviceKey.js +6 -0
  22. package/lib/module/sessionManager.js +6 -0
  23. package/lib/module/sessionToken.js +6 -0
  24. package/lib/module/syncCredential.js +6 -0
  25. package/lib/typescript/commonjs/LicenseManager.d.ts +26 -2
  26. package/lib/typescript/commonjs/config.d.ts +7 -0
  27. package/lib/typescript/commonjs/devToken.d.ts +27 -0
  28. package/lib/typescript/commonjs/headless.d.ts +9 -0
  29. package/lib/typescript/commonjs/hooks.d.ts +11 -0
  30. package/lib/typescript/commonjs/index.d.ts +7 -0
  31. package/lib/typescript/commonjs/index.web.d.ts +7 -0
  32. package/lib/typescript/commonjs/nodeSession.d.ts +86 -0
  33. package/lib/typescript/commonjs/serviceKey.d.ts +55 -0
  34. package/lib/typescript/commonjs/sessionManager.d.ts +170 -0
  35. package/lib/typescript/commonjs/sessionToken.d.ts +64 -0
  36. package/lib/typescript/commonjs/syncCredential.d.ts +11 -0
  37. package/lib/typescript/module/LicenseManager.d.ts +26 -2
  38. package/lib/typescript/module/config.d.ts +7 -0
  39. package/lib/typescript/module/devToken.d.ts +27 -0
  40. package/lib/typescript/module/headless.d.ts +9 -0
  41. package/lib/typescript/module/hooks.d.ts +11 -0
  42. package/lib/typescript/module/index.d.ts +7 -0
  43. package/lib/typescript/module/index.web.d.ts +7 -0
  44. package/lib/typescript/module/nodeSession.d.ts +86 -0
  45. package/lib/typescript/module/serviceKey.d.ts +55 -0
  46. package/lib/typescript/module/sessionManager.d.ts +170 -0
  47. package/lib/typescript/module/sessionToken.d.ts +64 -0
  48. package/lib/typescript/module/syncCredential.d.ts +11 -0
  49. package/lib/web/chunks/LicenseManager-HPGEBROD.mjs +1 -0
  50. package/lib/web/chunks/LicenseManager-IDGPK6OM.mjs +1 -0
  51. package/lib/web/chunks/LicenseManager-KDM6XEPI.mjs +1 -0
  52. package/lib/web/chunks/LicenseManager-Q3JTVOGK.mjs +1 -0
  53. package/lib/web/chunks/LicenseManager-RXOJAIDG.mjs +1 -0
  54. package/lib/web/chunks/LicenseManager-UHFOJRQ6.mjs +1 -0
  55. package/lib/web/chunks/LicenseManager-XSLQCKDC.mjs +1 -0
  56. package/lib/web/chunks/LicenseManager-YI5LR3AY.mjs +1 -0
  57. package/lib/web/chunks/LicenseManager-YKUYIOKJ.mjs +1 -0
  58. package/lib/web/chunks/chunk-AC36O63C.mjs +73 -0
  59. package/lib/web/chunks/chunk-CMYC3OJ3.mjs +73 -0
  60. package/lib/web/chunks/chunk-CVVUFZAQ.mjs +73 -0
  61. package/lib/web/chunks/chunk-JPTOVVON.mjs +73 -0
  62. package/lib/web/chunks/chunk-JVWYYKXV.mjs +73 -0
  63. package/lib/web/chunks/chunk-KQ3HUCUX.mjs +73 -0
  64. package/lib/web/chunks/chunk-LHMH746X.mjs +73 -0
  65. package/lib/web/chunks/chunk-XOY6D2WL.mjs +73 -0
  66. package/lib/web/chunks/chunk-YEGY5MDO.mjs +73 -0
  67. package/lib/web/chunks/chunk-ZSTLYSNF.mjs +68 -68
  68. package/lib/web/index.mjs +6 -1
  69. package/package.json +1 -1
@@ -0,0 +1,6 @@
1
+ "use strict";/*!
2
+ * PROPRIETARY — Buoy license enforcement. NOT open source.
3
+ *
4
+ * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
+ * (https://buoy.gg/terms), not an OSS license.
6
+ */import{getLicenseStorage as p}from"./storage";import{base64urlEncode as f,verifySessionToken as m,SESSION_CLOCK_SKEW_SECONDS as g}from"./sessionToken.js";import{realNow as w}from"./realNow.js";export const SESSION_STORAGE_PREFIX="@buoy/session:";export const SESSION_OFFLINE_GRACE_MS=7*24*60*60*1e3;export const SESSION_REFRESH_LEAD_MS=6*60*60*1e3;export const SESSION_RETRY_MS=5*60*1e3;export const SESSION_CALL_TIMEOUT_MS=15e3;export const DEFAULT_SIGNIN_ORIGIN="https://buoy.gg";const y="buoy:signin";const T=10*60*1e3;export class SessionError extends Error{constructor(e,t){super(t);this.code=e;this.name="SessionError"}}function S(){const s=globalThis;return{fetch:(e,t)=>fetch(e,t),storage:p,now:w,random:e=>{const t=new Uint8Array(e);if(!s.crypto?.getRandomValues)throw new SessionError("not_configured","This platform has no secure random source.");return s.crypto.getRandomValues(t)},sha256:async e=>{if(!s.crypto?.subtle)throw new SessionError("not_configured","Sign-in needs a secure page (https or localhost).");return new Uint8Array(await s.crypto.subtle.digest("SHA-256",e))},openPopup:()=>{if(!s.window?.open)return null;const e=480;const t=640;const i=Math.max(0,(s.window.screenX??0)+((s.window.outerWidth??e)-e)/2);const n=Math.max(0,(s.window.screenY??0)+((s.window.outerHeight??t)-t)/2);return s.window.open("about:blank","buoy-signin",`popup,width=${e},height=${t},left=${i},top=${n}`)},onMessage:e=>{if(!s.window?.addEventListener)return()=>{};const t=i=>e({origin:i.origin,data:i.data});s.window.addEventListener("message",t);return()=>s.window?.removeEventListener("message",t)},withLock:(e,t)=>s.navigator?.locks?s.navigator.locks.request(e,t):t(),watch:e=>{const t=s.window;if(!t?.addEventListener)return()=>{};const i=r=>e.storage(r.key);const n=()=>{if(t.document?.visibilityState!=="hidden")e.wake()};t.addEventListener("storage",i);t.addEventListener("online",e.wake);t.document?.addEventListener?.("visibilitychange",n);return()=>{t.removeEventListener("storage",i);t.removeEventListener("online",e.wake);t.document?.removeEventListener?.("visibilitychange",n)}},setTimer:(e,t)=>setTimeout(e,Math.min(t,2147483647)),clearTimer:e=>clearTimeout(e)}}function k(){const s=globalThis.location;if(!s?.origin||!/^https?:$/.test(s.protocol??""))return void 0;return s.origin}export class SessionManagerImpl{signinOrigin=DEFAULT_SIGNIN_ORIGIN;current=null;claims=null;listeners=new Set;timer=null;refreshing=null;loaded=null;unwatch=null;limitTimer=null;epoch=0;constructor(e){this.deps=e??S();this.audience=k()}get origin(){return this.signinOrigin}configure(e){if(e.audience!==void 0)this.audience=e.audience;if(e.signinOrigin!==void 0)this.signinOrigin=e.signinOrigin.replace(/\/$/,"");this.reset()}__setDeps(e){this.deps=e;this.reset()}reset(){this.unwatch?.();this.unwatch=null;this.epoch+=1;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;this.current=null;this.claims=null;this.loaded=null;this.refreshing=null}get storageKey(){return SESSION_STORAGE_PREFIX+(this.audience??"")}subscribe(e){this.listeners.add(e);return()=>this.listeners.delete(e)}notify(){for(const e of[...this.listeners]){try{e()}catch(t){console.warn("[Buoy] sign-in listener error:",t)}}}getAccessToken(){const e=this.getStatus();return e.state==="active"&&!e.grace?this.current?.accessToken??null:null}getStatus(){const e=this.claims;if(!e)return{state:"signed_out"};const t=e.exp*1e3;const i=this.deps.now();if(i<(e.iat-g)*1e3)return{state:"signed_out"};if(i<t)return{state:"active",claims:e,grace:false,validUntil:t};if(i<t+SESSION_OFFLINE_GRACE_MS){return{state:"active",claims:e,grace:true,validUntil:t+SESSION_OFFLINE_GRACE_MS}}return{state:"signed_out"}}load(){if(!this.loaded)this.loaded=this.loadOnce();return this.loaded}async loadOnce(){if(!this.audience)return;this.unwatch??=this.deps.watch?.({storage:t=>{if(t===null||t===this.storageKey)void this.sync()},wake:()=>{if(this.claims)void this.refresh()}})??null;const e=await this.read();if(e&&this.adopt(e)){this.notify();this.schedule()}}async sync(){if(!this.audience)return;const e=await this.read();if(!e){if(this.current)this.clearLocal();return}if(e.refreshToken!==this.current?.refreshToken&&this.adopt(e)){this.notify();this.schedule()}}async read(){try{const e=await(await this.deps.storage()).getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t?.v===1&&typeof t.accessToken==="string"&&typeof t.refreshToken==="string"?t:null}catch{return null}}async write(e){const t=await this.deps.storage();if(e)await t.setItem(this.storageKey,JSON.stringify(e));else await t.removeItem(this.storageKey)}adopt(e){if(!this.audience)return false;const t=m(e.accessToken,{audience:this.audience,ignoreTime:true,publicKeyHex:this.deps.publicKeyHex});const i=t.ok?t.claims:null;if(!i)return false;this.current=e;this.claims=i;return true}armLimit(){if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(!this.claims)return;const e=this.claims.exp*1e3;const t=this.deps.now();const i=t<e?e:t<e+SESSION_OFFLINE_GRACE_MS?e+SESSION_OFFLINE_GRACE_MS:null;if(i===null)return;this.limitTimer=this.deps.setTimer(()=>{this.limitTimer=null;this.notify();this.armLimit()},i-t)}call(e,t){return new Promise((i,n)=>{const r=this.deps.setTimer(()=>n(new Error("timeout")),SESSION_CALL_TIMEOUT_MS);this.deps.fetch(e,t).then(o=>{this.deps.clearTimer(r);i(o)},o=>{this.deps.clearTimer(r);n(o)})})}schedule(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(!this.claims)return;const e=this.claims.exp*1e3-SESSION_REFRESH_LEAD_MS-this.deps.now();this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh()},Math.max(0,e))}refresh(e=false){if(!this.refreshing){this.refreshing=this.deps.withLock(this.lockName,()=>this.refreshLocked(e)).catch(()=>this.retryLater()).finally(()=>{this.refreshing=null})}return this.refreshing}get lockName(){return`buoy-session:${this.audience??""}`}async refreshLocked(e){const t=this.epoch;const i=await this.read();if(!i){if(this.current)this.clearLocal();return}if(i.refreshToken!==this.current?.refreshToken){if(this.adopt(i)){this.notify();this.schedule();return}}if(!this.claims)return;if(!e&&this.claims.exp*1e3-this.deps.now()>SESSION_REFRESH_LEAD_MS){this.schedule();return}let n;let r;try{const o=await this.call(`${this.signinOrigin}/api/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:i.refreshToken,audience:this.audience})});n=o.status;r=await o.json().catch(()=>null)}catch{this.retryLater();return}if(t!==this.epoch)return;if(n===200&&r&&typeof r.access_token==="string"&&typeof r.refresh_token==="string"){const o={v:1,accessToken:r.access_token,refreshToken:r.refresh_token};if(this.adopt(o)){await this.write(o);this.notify();this.schedule();return}}if(n===400||n===401||n===403){await this.write(null);this.clearLocal();return}this.retryLater()}retryLater(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh(true)},SESSION_RETRY_MS);this.notify()}clearLocal(){if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;this.current=null;this.claims=null;this.notify()}async signIn(){if(!this.audience)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const e=this.deps.openPopup();if(!e)throw new SessionError("popup_blocked","The browser blocked the sign-in window. Allow pop-ups for this site and try again.");try{return await this.signInWith((t,i)=>new Promise((n,r)=>{e.location.href=t;const o=this.deps.now();let c=null;const h=l=>{if(c!==null)this.deps.clearTimer(c);a();l()};const a=this.deps.onMessage(l=>{if(l.origin!==this.signinOrigin)return;const d=l.data;if(!d||d.type!==y||d.state!==i)return;if(typeof d.code==="string")h(()=>n(d.code));else h(()=>r(new SessionError("access_denied","Sign-in was cancelled.")))});const u=()=>{if(e.closed)return h(()=>r(new SessionError("popup_closed","The sign-in window was closed.")));if(this.deps.now()-o>T)return h(()=>r(new SessionError("timeout","Sign-in took too long.")));c=this.deps.setTimer(u,500)};c=this.deps.setTimer(u,500)}))}finally{try{if(!e.closed)e.close()}catch{}}}async signInWith(e){const t=this.audience;if(!t)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const i=f(this.deps.random(32));const n=f(this.deps.random(16));const r=f(await this.deps.sha256(new TextEncoder().encode(i)));const o=`${this.signinOrigin}/auth/authorize?audience=${encodeURIComponent(t)}&code_challenge=${r}&code_challenge_method=S256&state=${n}`;const c=await e(o,n);let h;try{h=await this.call(`${this.signinOrigin}/api/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:c,code_verifier:i,audience:t})})}catch{throw new SessionError("network","Buoy could not be reached. Check your connection and try again.")}const a=await h.json().catch(()=>null);if(h.status!==200||!a||typeof a.access_token!=="string"||typeof a.refresh_token!=="string"){const l=a&&typeof a.error_description==="string"?a.error_description:"Buoy did not accept this sign-in.";throw new SessionError(h.status===503?"unavailable":"rejected",l)}const u={v:1,accessToken:a.access_token,refreshToken:a.refresh_token};this.epoch+=1;if(!this.adopt(u))throw new SessionError("rejected","Buoy sent a sign-in this app could not check.");await this.write(u);await this.load();this.notify();this.schedule();return this.getStatus()}async signOut(){this.epoch+=1;const e=this.current;this.clearLocal();let t=e;try{await this.deps.withLock(this.lockName,async()=>{t=await this.read()??e;await this.write(null)})}catch{await this.write(null).catch(()=>{})}if(!t)return;try{await this.call(`${this.signinOrigin}/api/auth/revoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:t.refreshToken})})}catch{}}}export const SessionManager=new SessionManagerImpl;
@@ -0,0 +1,6 @@
1
+ "use strict";/*!
2
+ * PROPRIETARY — Buoy Pro license enforcement. NOT open source.
3
+ *
4
+ * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
+ * (https://buoy.gg/terms), not an OSS license.
6
+ */import c from"tweetnacl";import{SESSION_TOKEN_PUBLIC_KEY as m}from"./config.js";import{realNow as T}from"./realNow.js";export const SESSION_TOKEN_PREFIX="buoys1";const v="buoy-session-v1:";const y=1;export const SESSION_CLOCK_SKEW_SECONDS=60;const _=64;const N=["free","pro","business"];const s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";export function base64urlEncode(t){let e="";let r=0;for(;r+2<t.length;r+=3){const n=t[r]<<16|t[r+1]<<8|t[r+2];e+=s[n>>>18&63]+s[n>>>12&63]+s[n>>>6&63]+s[n&63]}const o=t.length-r;if(o===1){const n=t[r]<<16;e+=s[n>>>18&63]+s[n>>>12&63]}else if(o===2){const n=t[r]<<16|t[r+1]<<8;e+=s[n>>>18&63]+s[n>>>12&63]+s[n>>>6&63]}return e}function g(t){if(!/^[A-Za-z0-9_-]*$/.test(t)||t.length%4===1)return null;const e=[];let r=0;let o=0;for(const n of t){o=o<<6|s.indexOf(n);r+=6;if(r>=8){e.push(o>>>r-8&255);r-=8}}return new Uint8Array(e)}function h(t){if(typeof TextEncoder!=="undefined")return new TextEncoder().encode(t);const e=unescape(encodeURIComponent(t));const r=new Uint8Array(e.length);for(let o=0;o<e.length;o+=1)r[o]=e.charCodeAt(o);return r}function O(t){try{if(typeof TextDecoder!=="undefined")return new TextDecoder("utf-8",{fatal:true}).decode(t);let e="";for(const r of t)e+=String.fromCharCode(r);return decodeURIComponent(escape(e))}catch{return null}}function S(t){const e=t.trim().toLowerCase();const r=new Uint8Array(e.length/2);for(let o=0;o<r.length;o+=1)r[o]=parseInt(e.slice(o*2,o*2+2),16);return r}function k(t){return h(v+t)}function l(t,e=256){return typeof t==="string"&&t.length>0&&t.length<=e}function x(t){return typeof t==="number"&&Number.isInteger(t)&&t>0}function C(t){if(!t||typeof t!=="object"||Array.isArray(t))return null;const e=t;if(!l(e.sid)||!l(e.sub)||!l(e.aud,512))return null;if(e.team!==null&&!l(e.team))return null;if(!N.includes(e.tier))return null;if(e.role!==void 0&&!l(e.role,64))return null;if(e.tools!==void 0&&(!Array.isArray(e.tools)||e.tools.length>256||!e.tools.every(r=>l(r,64)))){return null}if(!x(e.iat)||!x(e.exp)||e.exp<=e.iat)return null;return{v:y,sid:e.sid,sub:e.sub,team:e.team,tier:e.tier,...e.role!==void 0?{role:e.role}:{},...e.tools!==void 0?{tools:[...e.tools]}:{},aud:e.aud,iat:e.iat,exp:e.exp}}export function mintSessionToken(t,e){const r=base64urlEncode(h(JSON.stringify(t)));const{secretKey:o}=c.sign.keyPair.fromSeed(S(e));const n=c.sign.detached(k(r),o);return`${SESSION_TOKEN_PREFIX}.${r}.${base64urlEncode(n)}`}let b=m;export function __setSessionTokenPublicKeyForTests(t){b=t??m}export function isSessionToken(t){return t.startsWith(`${SESSION_TOKEN_PREFIX}.`)}export function verifySessionToken(t,e){const r=e.publicKeyHex??b;if(!r)return{ok:false,reason:"no-key"};const o=typeof t==="string"?t.split("."):[];if(o.length!==3||o[0]!==SESSION_TOKEN_PREFIX)return{ok:false,reason:"malformed"};const[,n,E]=o;const a=g(n);const f=g(E);if(!a||!f||f.length!==_)return{ok:false,reason:"malformed"};try{if(!c.sign.detached.verify(k(n),f,S(r))){return{ok:false,reason:"signature"}}}catch{return{ok:false,reason:"signature"}}const d=O(a);let i;try{i=d===null?null:JSON.parse(d)}catch{return{ok:false,reason:"malformed"}}if(!i||typeof i!=="object"||i.v!==y){return{ok:false,reason:i&&typeof i==="object"?"version":"malformed"}}const u=C(i);if(!u)return{ok:false,reason:"claims"};if(u.aud!==e.audience)return{ok:false,reason:"audience"};if(e.ignoreTime)return{ok:true,claims:u};const p=Math.floor((e.nowMs??T())/1e3);if(p>=u.exp+SESSION_CLOCK_SKEW_SECONDS)return{ok:false,reason:"expired"};if(u.iat>p+SESSION_CLOCK_SKEW_SECONDS)return{ok:false,reason:"not-yet-valid"};return{ok:true,claims:u}}
@@ -0,0 +1,6 @@
1
+ "use strict";/*!
2
+ * PROPRIETARY — Buoy license enforcement. NOT open source.
3
+ *
4
+ * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
+ * (https://buoy.gg/terms), not an OSS license.
6
+ */import{LicenseManager as r}from"./LicenseManager.js";import{SessionManager as n}from"./sessionManager.js";import{checkDevToken as t,isDevToken as i}from"./devToken.js";export function getSyncCredential(){const o=n.getAccessToken();if(o)return{token:o,origin:n.origin};const e=r.getState().licenseKey;if(e&&i(e)&&r.hasAccountAccess()&&t(e).ok){return{token:e,origin:n.origin}}return null}
@@ -14,7 +14,7 @@
14
14
  * including a free one (`npx buoy login`). Buy a key at
15
15
  * https://buoy.gg or use it free on Saturday and Sunday.
16
16
  */
17
- import type { BuoyTier, LicenseListener } from "./types";
17
+ import type { BuoyTier, TierSource, LicenseListener } from "./types";
18
18
  /** Info passed to device registration callback */
19
19
  export interface DeviceRegistrationInfo {
20
20
  fingerprint: string;
@@ -47,6 +47,7 @@ declare class LicenseManagerImpl {
47
47
  private verifiedAt;
48
48
  private expiryTimer;
49
49
  private removeForegroundListener;
50
+ private sessionUnsubscribe;
50
51
  /**
51
52
  * Pending backoff retry after a transient failure (see
52
53
  * LICENSE_TRANSIENT_RETRY_DELAYS). At most one is ever outstanding, and it
@@ -101,6 +102,18 @@ declare class LicenseManagerImpl {
101
102
  * launch and the account-admission clock see a validated key, and marked
102
103
  * quiet so nothing ever revalidates it against a server.
103
104
  */
105
+ /**
106
+ * A Business team's bot key (serviceKey.ts). Admits as Pro until the token
107
+ * runs out and trades again an hour before; a deleted key locks at the next
108
+ * trade. Not cached on disk: CI machines start fresh anyway.
109
+ */
110
+ private admitServiceKey;
111
+ /**
112
+ * A dev token (devToken.ts). Admits until it runs out: the account-check
113
+ * clock starts at its issue time, and both last 30 days. A shipped build
114
+ * refuses it, so a token that slips into one opens nothing.
115
+ */
116
+ private admitDevToken;
104
117
  private admitToken;
105
118
  /**
106
119
  * "Don't ask right now": serve the trusted cache if it vouches for this
@@ -134,6 +147,15 @@ declare class LicenseManagerImpl {
134
147
  * usable at all is `getAccountAccess()`.
135
148
  */
136
149
  getTier(): BuoyTier;
150
+ /**
151
+ * Which credential decides: the sign-in when there's no admitted key, or
152
+ * when it grants Pro and the key doesn't; otherwise the key.
153
+ */
154
+ private credential;
155
+ /** What the key alone grants. */
156
+ private keyTier;
157
+ /** What the sign-in alone grants, or null when signed out. Business reads as Pro here. */
158
+ private sessionTier;
137
159
  /**
138
160
  * Check if the user has Pro features
139
161
  */
@@ -147,18 +169,20 @@ declare class LicenseManagerImpl {
147
169
  */
148
170
  getState(): {
149
171
  tier: BuoyTier;
172
+ tierSource: TierSource;
150
173
  isPro: boolean;
151
174
  access: import("./accountAccess").AccountAccess;
152
175
  isInitialized: boolean;
153
176
  isValidating: boolean;
154
- tierSource: import("./types").TierSource;
155
177
  licenseKey: string | null;
156
178
  error: string | null;
157
179
  errorKind: "transport" | "verdict" | null;
158
180
  expiresAt: Date | null;
159
181
  };
160
182
  getAccountAccess(): import("./accountAccess").AccountAccess;
183
+ private getKeyAccess;
161
184
  hasAccountAccess(): boolean;
185
+ private hasKeyAccess;
162
186
  private setVerifiedAt;
163
187
  /**
164
188
  * Subscribe to license events
@@ -47,6 +47,13 @@ export declare const KEYGEN_CONFIG: {
47
47
  * Rotating it means a new SDK release, since this ships in the bundle.
48
48
  */
49
49
  export declare const FREE_TOKEN_PUBLIC_KEY = "3b546fc717a62683f3236c70167899a271bee3ffc04df6ec1eea60a394a2f1e9";
50
+ /**
51
+ * Ed25519 public key that sign-in access tokens, dev tokens and bot tokens
52
+ * are verified against (see sessionToken.ts and BUOY_SIGN_IN_PLAN.md). Not a
53
+ * secret. The private half lives only in the website Worker, as the secret
54
+ * BUOY_SESSION_PRIVATE_KEY (made 2026-10-02).
55
+ */
56
+ export declare const SESSION_TOKEN_PUBLIC_KEY = "36abba0267f7f379d992a5b6df641daab4190bcc9e639e36f1378e3c1ac52746";
50
57
  /**
51
58
  * Keygen policy IDs, which are what decide a key's tier.
52
59
  *
@@ -0,0 +1,27 @@
1
+ /*!
2
+ * PROPRIETARY — Buoy license enforcement. NOT open source.
3
+ *
4
+ * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
+ * (https://buoy.gg/terms), not an OSS license.
6
+ */
7
+ /**
8
+ * Dev tokens (BUOY_SIGN_IN_PLAN.md 2.3): what `npx buoy login` writes into a
9
+ * project for dev builds and sims. A sign-in token for the audience "dev",
10
+ * good for 30 days, checked offline. Only dev builds take it, so one that
11
+ * slips into a shipped app opens nothing there.
12
+ */
13
+ import { type SessionClaims } from "./sessionToken";
14
+ /** Keep in step with apps/website signin.server.ts DEV_AUDIENCE. */
15
+ export declare const DEV_AUDIENCE = "dev";
16
+ export declare function isDevToken(key: string): boolean;
17
+ export type DevTokenCheck = {
18
+ ok: true;
19
+ claims: SessionClaims;
20
+ } | {
21
+ ok: false;
22
+ reason: "invalid" | "expired";
23
+ message: string;
24
+ };
25
+ export declare function checkDevToken(token: string, now?: number): DevTokenCheck;
26
+ export declare const DEV_TOKEN_IN_RELEASE = "This is a dev token. It only works in dev builds. A live build turns on Buoy with Sign in with Buoy.";
27
+ //# sourceMappingURL=devToken.d.ts.map
@@ -7,4 +7,13 @@ export { resolveAccountAccess } from "./accountAccess";
7
7
  export { signCache, verifyCacheSignature } from "./cacheSignature";
8
8
  export { LICENSE_CACHE_DURATION, LICENSE_REVALIDATION_INTERVAL, LICENSE_LAUNCH_VALIDATION_TTL, LICENSE_TRANSIENT_RETRY_DELAYS, KEYGEN_CONFIG } from "./config";
9
9
  export type { CachedLicense, BuoyTier, KeygenValidationResponse } from "./types";
10
+ export { createNodeSession, fileSessionStorage, fileLock, receiveCodeOnLoopback, defaultSessionFile, DESKTOP_AUDIENCE, CLI_AUDIENCE, } from "./nodeSession";
11
+ export type { NodeSession, NodeSessionOptions, NodeSessionStorage } from "./nodeSession";
12
+ export { SessionError, SessionManagerImpl, SESSION_OFFLINE_GRACE_MS } from "./sessionManager";
13
+ export type { SessionStatus, SessionErrorCode } from "./sessionManager";
14
+ export { verifySessionToken, mintSessionToken, __setSessionTokenPublicKeyForTests } from "./sessionToken";
15
+ export type { SessionClaims } from "./sessionToken";
16
+ export { exchangeServiceKey, isServiceKey, normalizeLicenseKey, sameLicenseKey, SERVICE_KEY_PREFIX, SERVICE_AUDIENCE } from "./serviceKey";
17
+ export type { ServiceTokenResult } from "./serviceKey";
18
+ export { checkDevToken, isDevToken, DEV_AUDIENCE } from "./devToken";
10
19
  //# sourceMappingURL=headless.d.ts.map
@@ -17,6 +17,7 @@
17
17
  */
18
18
  import { LicenseManager } from "./LicenseManager";
19
19
  import type { BuoyTier, LicenseState } from "./types";
20
+ import { type SessionStatus } from "./sessionManager";
20
21
  /**
21
22
  * Why the user currently has Pro access:
22
23
  * - "licensed" → a real, validated license key
@@ -87,4 +88,14 @@ export declare function useProAccess(): {
87
88
  isWeekendFree: boolean;
88
89
  reason: ProAccessReason;
89
90
  };
91
+ /**
92
+ * The "Sign in with Buoy" state for this site, plus the actions a sign-in
93
+ * button needs. Call `signIn` straight from a click handler: the popup must
94
+ * open before anything awaits, or the browser blocks it.
95
+ */
96
+ export declare function useBuoySession(): {
97
+ status: SessionStatus;
98
+ signIn: () => Promise<SessionStatus>;
99
+ signOut: () => Promise<void>;
100
+ };
90
101
  //# sourceMappingURL=hooks.d.ts.map
@@ -33,6 +33,13 @@ export type { ProAccessReason } from "./hooks";
33
33
  export { useAccountAccess } from "./hooks";
34
34
  export { resolveAccountAccess, type AccountAccess } from "./accountAccess";
35
35
  export { LicenseTransportError, isTransientLicenseError } from "./api";
36
+ export { SessionManager, SessionError } from "./sessionManager";
37
+ export type { SessionStatus, SessionErrorCode, SessionConfig } from "./sessionManager";
38
+ export type { SessionClaims, SessionTier } from "./sessionToken";
39
+ export { useBuoySession } from "./hooks";
40
+ export { isServiceKey, normalizeLicenseKey, sameLicenseKey } from "./serviceKey";
41
+ export { isDevToken } from "./devToken";
42
+ export { getSyncCredential } from "./syncCredential";
36
43
  export { isBuoyToken } from "./freeToken";
37
44
  export { isWeekendFreePro, useWeekendFreePro } from "./weekendPro";
38
45
  export type { BuoyTier, TierSource, LicenseState, LicenseEvent, LicenseListener, LicenseValidationResult, CachedLicense, } from "./types";
@@ -22,6 +22,13 @@ export type { ProAccessReason } from "./hooks";
22
22
  export { useAccountAccess } from "./hooks";
23
23
  export { resolveAccountAccess, type AccountAccess } from "./accountAccess";
24
24
  export { LicenseTransportError, isTransientLicenseError } from "./api";
25
+ export { SessionManager, SessionError } from "./sessionManager";
26
+ export type { SessionStatus, SessionErrorCode, SessionConfig } from "./sessionManager";
27
+ export type { SessionClaims, SessionTier } from "./sessionToken";
28
+ export { useBuoySession } from "./hooks";
29
+ export { isServiceKey, normalizeLicenseKey, sameLicenseKey } from "./serviceKey";
30
+ export { isDevToken } from "./devToken";
31
+ export { getSyncCredential } from "./syncCredential";
25
32
  export { isBuoyToken } from "./freeToken";
26
33
  export { isWeekendFreePro, useWeekendFreePro } from "./weekendPro";
27
34
  export type { BuoyTier, TierSource, LicenseState, LicenseEvent, LicenseListener, LicenseValidationResult, CachedLicense, } from "./types";
@@ -0,0 +1,86 @@
1
+ /*!
2
+ * PROPRIETARY — Buoy license enforcement. NOT open source.
3
+ *
4
+ * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
+ * (https://buoy.gg/terms), not an OSS license.
6
+ */
7
+ import { SessionManagerImpl, type SessionDeps, type SessionStatus } from "./sessionManager";
8
+ /** First-party app ids buoy.gg signs in without a site list. Keep in step with apps/website signin.server.ts. */
9
+ export declare const DESKTOP_AUDIENCE = "app:gg.buoy.desktop";
10
+ export declare const CLI_AUDIENCE = "app:gg.buoy.cli";
11
+ export interface NodeSessionStorage {
12
+ getItem(key: string): Promise<string | null>;
13
+ setItem(key: string, value: string): Promise<void>;
14
+ removeItem(key: string): Promise<void>;
15
+ }
16
+ /** The default session file for the CLI and the MCP. */
17
+ export declare function defaultSessionFile(): string;
18
+ /** One JSON file holding every audience's pair, written 0600 via a temp file + rename. */
19
+ export declare function fileSessionStorage(file: string): NodeSessionStorage;
20
+ /**
21
+ * A cross-process lock: an O_EXCL lock file holding its owner's token.
22
+ *
23
+ * Only a lock older than LOCK_STALE_MS is taken over (its owner died: a
24
+ * refresh holds it for one web call, capped at 15 s). Waiting too long gives
25
+ * up instead, and the caller retries later. Only the owner removes the file,
26
+ * so a slow owner never deletes someone else's lock.
27
+ */
28
+ export declare function fileLock(lockFile: string, limits?: {
29
+ staleMs?: number;
30
+ waitMs?: number;
31
+ }): SessionDeps["withLock"];
32
+ /**
33
+ * Waits on 127.0.0.1 for buoy.gg's consent page to POST the code. Resolves
34
+ * with the URL to open (with `&port=`) and a promise for the code.
35
+ */
36
+ export declare function receiveCodeOnLoopback(baseUrl: string, state: string, signinOrigin: string, options?: {
37
+ timeoutMs?: number;
38
+ signal?: AbortSignal;
39
+ }): Promise<{
40
+ url: string;
41
+ code: Promise<string>;
42
+ }>;
43
+ export interface NodeSessionOptions {
44
+ audience: string;
45
+ /** Opens the sign-in page in the person's browser. */
46
+ openBrowser: (url: string) => void | Promise<void>;
47
+ storage?: NodeSessionStorage;
48
+ /** Lock file for refreshes. Default: next to the session file. */
49
+ lockFile?: string;
50
+ signinOrigin?: string;
51
+ fetch?: SessionDeps["fetch"];
52
+ now?: () => number;
53
+ /** Test seam for the verifier's key. */
54
+ publicKeyHex?: string;
55
+ }
56
+ export interface NodeSession {
57
+ manager: SessionManagerImpl;
58
+ load(): Promise<SessionStatus>;
59
+ getStatus(): SessionStatus;
60
+ /** Opens the browser and waits for the person to approve. */
61
+ signIn(options?: {
62
+ signal?: AbortSignal;
63
+ timeoutMs?: number;
64
+ }): Promise<SessionStatus>;
65
+ /**
66
+ * Trades the sign-in for a 30-day dev token, what `buoy login` writes into
67
+ * a project for dev builds (devToken.ts).
68
+ */
69
+ devToken(): Promise<{
70
+ ok: true;
71
+ token: string;
72
+ tier: string;
73
+ } | {
74
+ ok: false;
75
+ detail: string;
76
+ }>;
77
+ signOut(): Promise<void>;
78
+ refresh(force?: boolean): Promise<void>;
79
+ /** Re-read the session file (another process may have signed in or out). */
80
+ sync(): Promise<void>;
81
+ /** Whether buoy.gg has sign-in turned on. False when it can't be reached. */
82
+ isAvailable(): Promise<boolean>;
83
+ subscribe(listener: () => void): () => void;
84
+ }
85
+ export declare function createNodeSession(options: NodeSessionOptions): NodeSession;
86
+ //# sourceMappingURL=nodeSession.d.ts.map
@@ -0,0 +1,55 @@
1
+ /*!
2
+ * PROPRIETARY — Buoy license enforcement. NOT open source.
3
+ *
4
+ * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
+ * (https://buoy.gg/terms), not an OSS license.
6
+ */
7
+ /**
8
+ * Team keys for bots and CI (BUOY_SIGN_IN_PLAN.md 4.4).
9
+ *
10
+ * A Business team admin makes a `bsk1_…` key on buoy.gg. A CI build passes
11
+ * it where a license key goes. It is traded at buoy.gg for a token that lasts
12
+ * a day and is checked offline like any sign-in token; deleting the key on
13
+ * buoy.gg stops the next trade, so a bot stops within a day.
14
+ */
15
+ import { type SessionClaims } from "./sessionToken";
16
+ export declare const SERVICE_KEY_PREFIX = "bsk1_";
17
+ /** Keep in step with apps/website signin.server.ts SERVICE_AUDIENCE. */
18
+ export declare const SERVICE_AUDIENCE = "service";
19
+ export declare function isServiceKey(key: string): boolean;
20
+ /**
21
+ * A license key as typed: trimmed, and upper-cased unless it is a bot key or
22
+ * a dev token (`buoys1.…`), whose letters are case-sensitive.
23
+ */
24
+ export declare function normalizeLicenseKey(key: string): string;
25
+ /**
26
+ * Whether two typed keys are the same key, compared the way LicenseManager
27
+ * stores them. Use this, never a plain toUpperCase(): bot keys and dev tokens
28
+ * are case-sensitive.
29
+ */
30
+ export declare function sameLicenseKey(a: string | null | undefined, b: string | null | undefined): boolean;
31
+ export type ServiceTokenResult = {
32
+ ok: true;
33
+ claims: SessionClaims;
34
+ } | {
35
+ ok: false;
36
+ reason: "network" | "rejected";
37
+ detail: string;
38
+ };
39
+ type FetchLike = (url: string, init: {
40
+ method: string;
41
+ headers: Record<string, string>;
42
+ body: string;
43
+ signal?: AbortSignal;
44
+ }) => Promise<{
45
+ status: number;
46
+ json(): Promise<unknown>;
47
+ }>;
48
+ /** Trades a bot key for a token and checks it. Never throws. */
49
+ export declare function exchangeServiceKey(key: string, options: {
50
+ origin: string;
51
+ fetch?: FetchLike;
52
+ publicKeyHex?: string;
53
+ }): Promise<ServiceTokenResult>;
54
+ export {};
55
+ //# sourceMappingURL=serviceKey.d.ts.map
@@ -0,0 +1,170 @@
1
+ /*!
2
+ * PROPRIETARY — Buoy license enforcement. NOT open source.
3
+ *
4
+ * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
+ * (https://buoy.gg/terms), not an OSS license.
6
+ */
7
+ import { type SessionClaims } from "./sessionToken";
8
+ export declare const SESSION_STORAGE_PREFIX = "@buoy/session:";
9
+ /** How long an expired access token still counts while buoy.gg is unreachable. */
10
+ export declare const SESSION_OFFLINE_GRACE_MS: number;
11
+ /** Refresh once less than this is left on the access token (they last 12 h). */
12
+ export declare const SESSION_REFRESH_LEAD_MS: number;
13
+ /** Retry delay after buoy.gg could not be reached. */
14
+ export declare const SESSION_RETRY_MS: number;
15
+ /** A web call to buoy.gg that takes longer than this counts as offline. */
16
+ export declare const SESSION_CALL_TIMEOUT_MS = 15000;
17
+ export declare const DEFAULT_SIGNIN_ORIGIN = "https://buoy.gg";
18
+ export type SessionStatus = {
19
+ state: "signed_out";
20
+ } | {
21
+ state: "active";
22
+ claims: SessionClaims;
23
+ /** True when the access token has run out and buoy.gg could not be reached to renew it. */
24
+ grace: boolean;
25
+ /** ms; when access stops unless a refresh lands first. */
26
+ validUntil: number;
27
+ };
28
+ export type SessionErrorCode = "not_configured" | "popup_blocked" | "popup_closed" | "access_denied" | "timeout" | "rejected" | "unavailable" | "network";
29
+ export declare class SessionError extends Error {
30
+ code: SessionErrorCode;
31
+ constructor(code: SessionErrorCode, message: string);
32
+ }
33
+ interface StorageLike {
34
+ getItem(key: string): Promise<string | null>;
35
+ setItem(key: string, value: string): Promise<void>;
36
+ removeItem(key: string): Promise<void>;
37
+ }
38
+ interface PopupLike {
39
+ closed: boolean;
40
+ close(): void;
41
+ location: {
42
+ href: string;
43
+ };
44
+ }
45
+ /** Everything platform-specific, injectable for tests. */
46
+ export interface SessionDeps {
47
+ fetch: (url: string, init: {
48
+ method: string;
49
+ headers: Record<string, string>;
50
+ body?: string;
51
+ }) => Promise<{
52
+ status: number;
53
+ json(): Promise<unknown>;
54
+ }>;
55
+ storage: () => Promise<StorageLike>;
56
+ now: () => number;
57
+ random: (length: number) => Uint8Array;
58
+ sha256: (bytes: Uint8Array) => Promise<Uint8Array>;
59
+ /** Opens a blank popup; null when blocked or not a browser. */
60
+ openPopup: () => PopupLike | null;
61
+ /** Subscribes to window messages. */
62
+ onMessage: (handler: (event: {
63
+ origin: string;
64
+ data: unknown;
65
+ }) => void) => () => void;
66
+ /** Runs `task` while holding a lock shared by every tab of this site. */
67
+ withLock: <T>(name: string, task: () => Promise<T>) => Promise<T>;
68
+ setTimer: (fn: () => void, ms: number) => unknown;
69
+ clearTimer: (handle: unknown) => void;
70
+ /** Test seam for the verifier's key. */
71
+ publicKeyHex?: string;
72
+ /**
73
+ * Browser only: `storage` when another tab changed saved data (key, or
74
+ * null for "all cleared"), `wake` when the page is shown again or comes
75
+ * back online. Node hosts poll `sync()` instead.
76
+ */
77
+ watch?: (on: {
78
+ storage: (key: string | null) => void;
79
+ wake: () => void;
80
+ }) => () => void;
81
+ }
82
+ export interface SessionConfig {
83
+ /** Origin ("https://app.acme.com") or app id ("app:com.acme.app"). Web default: this page's origin. */
84
+ audience?: string;
85
+ /** Where buoy.gg lives. Only tests and local development change this. */
86
+ signinOrigin?: string;
87
+ }
88
+ type Listener = () => void;
89
+ export declare class SessionManagerImpl {
90
+ private deps;
91
+ private audience;
92
+ private signinOrigin;
93
+ private current;
94
+ private claims;
95
+ private listeners;
96
+ private timer;
97
+ private refreshing;
98
+ private loaded;
99
+ private unwatch;
100
+ /** Fires when access or the offline grace runs out, so the UI hears it. */
101
+ private limitTimer;
102
+ /**
103
+ * Bumped by sign-in and sign-out. A refresh that started before either
104
+ * drops its result: a late reply must not sign a signed-out person back in.
105
+ */
106
+ private epoch;
107
+ constructor(deps?: SessionDeps);
108
+ /** Where buoy.gg is. Bot keys (serviceKey.ts) are traded there too. */
109
+ get origin(): string;
110
+ /** Change the audience or buoy.gg origin. Drops what is loaded; call load() again. */
111
+ configure(config: SessionConfig): void;
112
+ /** @internal tests */
113
+ __setDeps(deps: SessionDeps): void;
114
+ private reset;
115
+ private get storageKey();
116
+ subscribe(listener: Listener): () => void;
117
+ private notify;
118
+ /**
119
+ * The current access token, for trading at buoy.gg (the CLI gets a dev
120
+ * token with it). Null when signed out or past the token's end.
121
+ */
122
+ getAccessToken(): string | null;
123
+ getStatus(): SessionStatus;
124
+ /** Reads the saved sign-in, if any, and starts keeping it fresh. Safe to call often. */
125
+ load(): Promise<void>;
126
+ private loadOnce;
127
+ /**
128
+ * Pick up a sign-in or sign-out another process or tab wrote. Cheap: one
129
+ * storage read, no web call, no lock.
130
+ */
131
+ sync(): Promise<void>;
132
+ private read;
133
+ private write;
134
+ /**
135
+ * Take a stored pair as the current one when its access token is genuine
136
+ * and for this audience. Its time is judged by getStatus(), not here, so an
137
+ * expired token still loads and can be refreshed or used in grace.
138
+ */
139
+ private adopt;
140
+ /** Re-arms the timer that tells listeners when access or grace ends. */
141
+ private armLimit;
142
+ /** deps.fetch with a time limit; a call that hangs counts as offline. */
143
+ private call;
144
+ private schedule;
145
+ /**
146
+ * Swap the refresh token for a new pair when it's due (or `force`).
147
+ * Never throws: failures land in getStatus().
148
+ */
149
+ refresh(force?: boolean): Promise<void>;
150
+ private get lockName();
151
+ private refreshLocked;
152
+ private retryLater;
153
+ private clearLocal;
154
+ /**
155
+ * Open the buoy.gg popup and sign in. Call it straight from a click
156
+ * handler: the popup opens before the first await.
157
+ */
158
+ signIn(): Promise<SessionStatus>;
159
+ /**
160
+ * The sign-in flow with the code delivery left to the caller: a popup on
161
+ * the web (signIn above), a loopback port in Node (nodeSession.ts).
162
+ * `getCode` gets the buoy.gg URL to open and the state it must echo.
163
+ */
164
+ signInWith(getCode: (url: string, state: string) => Promise<string>): Promise<SessionStatus>;
165
+ /** Sign out here and end the sign-in on buoy.gg. */
166
+ signOut(): Promise<void>;
167
+ }
168
+ export declare const SessionManager: SessionManagerImpl;
169
+ export {};
170
+ //# sourceMappingURL=sessionManager.d.ts.map
@@ -0,0 +1,64 @@
1
+ /*!
2
+ * PROPRIETARY — Buoy Pro license enforcement. NOT open source.
3
+ *
4
+ * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
+ * (https://buoy.gg/terms), not an OSS license.
6
+ */
7
+ export declare const SESSION_TOKEN_PREFIX = "buoys1";
8
+ /** Tolerated difference between the device clock and buoy.gg's, in seconds. */
9
+ export declare const SESSION_CLOCK_SKEW_SECONDS = 60;
10
+ declare const TIERS: readonly ["free", "pro", "business"];
11
+ export type SessionTier = (typeof TIERS)[number];
12
+ export interface SessionClaims {
13
+ /** Format version. */
14
+ v: 1;
15
+ /** Sign-in id: every token from one sign-in shares it, so it can be revoked as one. */
16
+ sid: string;
17
+ /** The Buoy account. */
18
+ sub: string;
19
+ /** The team, or null for a personal account. */
20
+ team: string | null;
21
+ tier: SessionTier;
22
+ /** Team role, when the team assigns one. */
23
+ role?: string;
24
+ /** Tools this person may use, when the team limits them. */
25
+ tools?: string[];
26
+ /** The site origin (e.g. "https://app.acme.com") or app id ("app:com.acme.app") it was issued for. */
27
+ aud: string;
28
+ /** Issued at, seconds. */
29
+ iat: number;
30
+ /** Expires at, seconds. */
31
+ exp: number;
32
+ }
33
+ export type SessionVerifyFailure = "malformed" | "no-key" | "signature" | "version" | "claims" | "audience" | "expired" | "not-yet-valid";
34
+ export type SessionVerifyResult = {
35
+ ok: true;
36
+ claims: SessionClaims;
37
+ } | {
38
+ ok: false;
39
+ reason: SessionVerifyFailure;
40
+ };
41
+ export declare function base64urlEncode(bytes: Uint8Array): string;
42
+ /**
43
+ * Issue a token. Server-side only: needs the private key (32-byte seed, hex).
44
+ * buoy.gg has its own copy (sessionToken.server.ts); this one is for tests.
45
+ */
46
+ export declare function mintSessionToken(claims: SessionClaims, privateKeyHex: string): string;
47
+ /** @internal test seam: tests sign with a key they hold the private half of. */
48
+ export declare function __setSessionTokenPublicKeyForTests(hex: string | null): void;
49
+ /** Whether a string has the shape of a sign-in token (not whether it is valid). */
50
+ export declare function isSessionToken(value: string): boolean;
51
+ /**
52
+ * Check a token offline: signature, shape, audience and time. The clock is
53
+ * the device's real one (realNow), so @buoy-gg/clock moving the app's time
54
+ * can't expire or revive a sign-in.
55
+ */
56
+ export declare function verifySessionToken(token: string, options: {
57
+ audience: string;
58
+ nowMs?: number;
59
+ publicKeyHex?: string;
60
+ /** Check signature, shape and audience only. For callers that judge time themselves (sessionManager.ts offline grace). */
61
+ ignoreTime?: boolean;
62
+ }): SessionVerifyResult;
63
+ export {};
64
+ //# sourceMappingURL=sessionToken.d.ts.map
@@ -0,0 +1,11 @@
1
+ /*!
2
+ * PROPRIETARY — Buoy license enforcement. NOT open source.
3
+ *
4
+ * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
+ * (https://buoy.gg/terms), not an OSS license.
6
+ */
7
+ export declare function getSyncCredential(): {
8
+ token: string;
9
+ origin: string;
10
+ } | null;
11
+ //# sourceMappingURL=syncCredential.d.ts.map