Compare commits

..

9 commits

Author SHA1 Message Date
984ae9d71e fix(access): reset idle timer on activity + add hard session cap
The idle re-lock was an XState `after` on `idle`, which is anchored to
state ENTRY and never reset on screen touches — so it fired a fixed 60s
countdown regardless of interaction (reported: touching the screen
didn't extend the session). The machine can't observe raw pointer
events, so inactivity can't be measured there.

Move session timeouts to the DOM layer (useSessionSecurity, mounted in
the always-on App shell), enforcing two fail-closed limits that both
re-lock via a new root-level END_SESSION transition:

- SOFT idle (60s): re-lock after no *trusted* pointer/touch/key input
  while on the idle menu; resets on every genuine interaction. Scoped to
  idle so it never interrupts an in-flight cash-in/out.
- HARD cap (10min): absolute ceiling from unlock time, never reset — a
  forgotten/relayed card can't hold a session open. Lives at the machine
  root so it can lock mid-transaction, not just from idle.

Security posture: only event.isTrusted resets the soft timer (synthetic
events can't keep a session alive); wall-clock deadline checks re-lock
immediately after a suspend/resume rather than silently extending;
one-shot disarm-on-fire prevents spin; END_SESSION is guarded to the
active gate so it's inert when the gate is off.

Machine no longer owns the idle timer; tests updated (END_SESSION
re-locks from idle and from an in-flight cash-out; no-op when disabled).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ivBosaWmv8vwFE7ejrdHW
2026-08-16 09:12:22 +02:00
d1105bbb38 fix(access): put End Session ✕ left of Help, solid destructive red
Per on-device review: order the top-left group ✕ then ?, and use the
`destructive` button variant so the exit swatch is a solid, theme-aware
red (--destructive is scoped per colorscheme) rather than a subtle
outline that didn't read as an exit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ivBosaWmv8vwFE7ejrdHW
2026-08-16 08:47:22 +02:00
24515df35a fix(access): move End Session to a red ✕ beside Help (top-left)
The top-right "End Session" button overlapped the centered balance /
commission chips, which wrap into the top-right corner on narrower
screens (sintra). Relocate it to a minimal red ✕ icon button grouped
next to the "?" help button in the top-left, clear of the chips. Same
endSession() behavior; shown only while the gate is active.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ivBosaWmv8vwFE7ejrdHW
2026-08-16 08:34:00 +02:00
a4e3b2ccc8 feat(access): add End Session button to re-lock a tap-in session
A Bolt Card tap loads the holder's card for the whole session, so an
unattended idle menu is transactable by the next person until the 60s
IDLE_LOCK_TIMEOUT fires. Give the holder an explicit re-lock:

- END_SESSION event on `idle`, guarded to the active gate, targets
  `locked` (whose entry already clears the access session + loaded card).
  No-op on a gate-disabled machine that rests at idle.
- endSession() store action; IdleView shows a destructive-styled
  "End Session" button top-right only while accessControl.enabled.
- Tests: END_SESSION re-locks when the gate is active; no-op when off.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ivBosaWmv8vwFE7ejrdHW
2026-08-15 12:30:45 +02:00
ac96921074 feat(deploy): enable pcscd on upboard (sintra/tejo) for NFC gate
The access gate (ADR-003, #86) only wired services.pcscd + the pcsc
polkit rule into batm3.nix, so the tap-to-enter reader was invisible on
upboard machines. Port the same device-agnostic wiring to upboard.nix
(HID Global OMNIKEY 5022, 076b:5022) so the gate works on the sintra dev
unit — and on tejo — when #86 lands on dev and the nightly upgrade pulls
it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ivBosaWmv8vwFE7ejrdHW
2026-08-15 11:01:11 +02:00
Patrick Mulligan
d4314adc52 feat(access): auto re-lock the idle menu after inactivity
An unlocked session left unattended (card tapped in, no transaction) stayed
at idle indefinitely, so anyone could then transact on the loaded card. Add
an IDLE_LOCK_TIMEOUT (60s) after-transition on idle → locked, guarded by
accessGateActive so a gate-disabled machine (which rests at idle) never
re-locks. Selecting cash-in/out leaves idle and cancels the timer; the store
clears the loaded Bolt Card on re-lock. Transaction flows already re-lock on
their own inactivity timeouts.
2026-08-07 01:44:10 +02:00
Patrick Mulligan
2c6c0f1a89 fix(access): reset router to home on re-lock so the reopened gate lands on idle
After a transaction with the gate enabled, the machine re-locks (cashOut/
cashIn → locked, not idle), so the view's isIdle watch never fires and the
router stays on /cash-out|/cash-in. When the next tap reopens the gate to
idle, the stale transaction view showed (e.g. a completed sell's collect
screen, stuck). Reset the route to / while locked (router-view hidden under
LockedView) so idle renders IdleView.
2026-08-06 22:58:49 +02:00
Patrick Mulligan
4f0e284a96 feat(access): Bolt Card tap-to-enter — load card into session, one-press Complete
Builds on the access gate: a single Bolt Card tap at the locked screen both
unlocks the terminal AND pre-loads the card, so buy/sell just need "Complete"
— no second tap. Reuses the #83/#84 payment paths verbatim.

Soft entry, verify-at-payment: the tap is parsed LOCALLY (external_id only) so
the single-use SUN p/c stay valid; the cryptographic check happens at Complete
when the stored lnurlw actually moves sats (withdraw for sell, lnurlp-pay for
buy). Open-enrollment, card-only (no npub-QR, no PIN) per product decision.

- services/access: `boltcard` credential (externalId + lnurlw) in the AccessScan
  union; canonicalId + open-enrollment/allow-list authorize; parseBoltcardLnurlw
  (local, no server). Only external_id is hashed — p/c never enter authorize.
- store: loadedBoltCard (session-scoped, cleared on re-lock); handleBoltCardEntry
  (tap while locked → authorize → grant + load); completeWithCard (routes to the
  existing tap handlers); NFC listener routes locked→enter.
- LockedView: card-only "Tap your Bolt Card" screen (dropped camera/npub-QR/PIN).
- CashIn/CashOutView: "Complete Purchase/Sale" button + card chip when loaded.
- tests: boltcard authorize + parseBoltcardLnurlw (17 access tests total).

Enabling the gate is a provisioning step (access.json enabled+openEnrollment);
other machines default off → unchanged.
2026-08-06 22:37:09 +02:00
Patrick Mulligan
ba4fcbce2d feat(access): access-control gate — npub-QR badge + PIN + dev bypass (ADR-003)
Squashed skeleton (was 11 commits on feat/access-control-skeleton) for a
clean rebase onto dev. Adds a `locked` gate the terminal boots into until a
credential is presented; opt-in and non-breaking (defaults off → boots
straight to idle as before).

- state-machine: `locked` state + ACCESS_GRANTED/ACCESS_DENIED/DEV_UNLOCK
  events + accessBypass/devUnlockAllowed guards (packages/state-machine).
- services/access: reader abstraction, npub+PIN authorize() (nostr-tools
  nip19; accepts nostr:/nprofile), camera npub-QR reader, mock reader.
- LockedView.vue + ColorModeToggle: branded viewfinder, PIN pad, denied
  reason, dev-unlock; camera off-by-default + idle return.
- store/main/electron.d.ts: seed gate config, grant/deny/devUnlock wiring,
  access.json provisioning (no rebuild), get-config surface.
- deploy: access.example.json + provision-access.sh; ADR-003.

Credential union is npub today; UID (NFC tap) is the next step.
2026-08-06 22:18:05 +02:00
27 changed files with 25 additions and 193 deletions

View file

@ -73,18 +73,6 @@ VITE_SPIRE_SEED=
# Show "Under Service" screen and block all transactions
# VITE_MAINTENANCE_MODE=true
# =============================================================================
# Public Web Demo
# =============================================================================
# Set ONLY for the browser demo build (atm.demo.aiolabs.dev). Leave blank on
# every real machine. When set it:
# - keeps the mouse cursor visible (kiosk builds hide it)
# - mints one extra, never-used LNbits wallet named with this exact string,
# so the throwaway accounts the demo creates (one per page load, each with
# its own ephemeral identity) can be swept by name instead of guessed at.
# VITE_DEMO_TAG=bitspire-web-demo
# =============================================================================
# Mock Fallback (Production Safety)
# =============================================================================

View file

@ -13,8 +13,6 @@
* QR path keeps working — cash-out never depends on this.
*/
import { execFile } from 'node:child_process'
export type NfcState = 'ready' | 'reading' | 'error' | 'card-removed' | 'unavailable'
export interface NfcStatus {
state: NfcState
@ -85,11 +83,7 @@ export async function readNdefLnurlw(
const send = (bytes: number[]) => transmit(Buffer.from(bytes), 256)
// Select the NDEF Tag Application (AID D2760000850101).
if (
!swOk(
await send([0x00, 0xa4, 0x04, 0x00, 0x07, 0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00])
)
) {
if (!swOk(await send([0x00, 0xa4, 0x04, 0x00, 0x07, 0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00]))) {
return null
}
@ -113,29 +107,6 @@ export async function readNdefLnurlw(
let stopFn: (() => void) | null = null
// ── Wedge auto-recovery ───────────────────────────────────────────────────
// Cheap CCID readers (the Feitian R502-CL especially) occasionally wedge: they
// keep detecting a card but every APDU returns "card absent or mute", and ONLY
// a USB power-cycle clears it — pcscd/app restarts do NOT. When we see a run of
// consecutive read failures we trigger nfc-reader-reset.service (a root oneshot
// that re-binds the reader's USB device = a software replug); nfc-pcsc then
// re-detects the reader on hotplug with no app restart. The trigger is gated by
// a cooldown so a still-wedged reader can't reset-loop. A quality reader (e.g.
// ACR1252U) wedges far less; this is belt-and-suspenders for any reader.
const WEDGE_FAILURE_THRESHOLD = 3
const RESET_COOLDOWN_MS = 30_000
// Persist across reader re-enumerations (a reset spawns a fresh reader closure).
let lastReaderResetAt = 0
/** Trigger the privileged USB power-cycle of the reader. Best-effort. */
function resetWedgedReader(): void {
// NixOS: the app runs unprivileged as `bitspire`; a polkit rule authorises it
// to start this one unit. systemctl lives at a stable path on the device.
execFile('/run/current-system/sw/bin/systemctl', ['start', 'nfc-reader-reset.service'], () => {
/* best-effort — if it fails the reader stays wedged until a manual reset */
})
}
/**
* Start listening for Bolt Card taps. Idempotent. Returns a stop function.
* Never throws — failures surface via onStatus.
@ -188,10 +159,6 @@ export async function startNfcReader(
// a present↔empty storm when hammered, so ignore re-detections for a beat
// after a failure. Successful reads don't cool down.
let cooldownUntil = 0
// Consecutive failed reads → wedge detection (see resetWedgedReader above).
// A completed read (Bolt Card or not) proves the reader is healthy and
// clears the count; only a run of thrown transmits trips the reset.
let consecutiveFailures = 0
r.on('card', async () => {
if (Date.now() < cooldownUntil) return
onStatus({ state: 'reading', reader: name })
@ -200,30 +167,13 @@ export async function startNfcReader(
// user simply re-taps.
try {
const lnurlw = await readNdefLnurlw((apdu, maxLen) => r.transmit(apdu, maxLen))
consecutiveFailures = 0
if (lnurlw) {
onCard(lnurlw)
return
}
onStatus({ state: 'error', reader: name, message: 'not a Bolt Card' })
} catch (e) {
consecutiveFailures++
if (
consecutiveFailures >= WEDGE_FAILURE_THRESHOLD &&
Date.now() - lastReaderResetAt > RESET_COOLDOWN_MS
) {
// Reader looks wedged — auto power-cycle it (only fix that works).
lastReaderResetAt = Date.now()
consecutiveFailures = 0
onStatus({ state: 'error', reader: name, message: 'reader stuck — auto-resetting…' })
resetWedgedReader()
} else {
onStatus({
state: 'error',
reader: name,
message: 'card read failed — hold steady & retap',
})
}
onStatus({ state: 'error', reader: name, message: 'card read failed — hold steady & retap' })
void e
}
cooldownUntil = Date.now() + 1500
@ -232,9 +182,7 @@ export async function startNfcReader(
r.on('error', (err: unknown) =>
onStatus({ state: 'error', reader: name, message: errMsg(err) })
)
r.on('end', () =>
onStatus({ state: 'unavailable', reader: name, message: 'reader disconnected' })
)
r.on('end', () => onStatus({ state: 'unavailable', reader: name, message: 'reader disconnected' }))
})
nfc.on('error', (err: unknown) => onStatus({ state: 'error', message: errMsg(err) }))

View file

@ -2,7 +2,7 @@
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/logo.png" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<!--
Content Security Policy:
@ -18,7 +18,7 @@
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss: http: https:; img-src 'self' data: blob:; font-src 'self'; frame-src 'none'; object-src 'none'"
/>
<title>bitSpire ATM</title>
<title>Lamassu ATM</title>
<style>
/* Prevent text selection and context menu on kiosk */
* {

View file

@ -15,7 +15,6 @@
"dev:vite": "vite",
"electron:dev": "tsc -p electron/tsconfig.json && tsc -p electron/tsconfig.preload.json && electron dist-electron/main.js",
"build": "vue-tsc --noEmit && vite build && tsc -p electron/tsconfig.json && tsc -p electron/tsconfig.preload.json && npx esbuild electron/fund-atm.ts --bundle --platform=node --format=cjs --external:better-sqlite3 --outfile=dist-electron/fund-atm.bundle.cjs",
"build:web": "vite build",
"build:electron": "pnpm build && electron-builder",
"preview": "vite preview",
"typecheck": "vue-tsc --noEmit",

View file

@ -24,13 +24,6 @@ const router = createRouter({
],
})
// Kiosk chrome (hidden cursor) is the default — every real machine is a
// touchscreen. The public web demo (VITE_DEMO_TAG) runs in a normal browser,
// where an invisible pointer just reads as broken.
if (!import.meta.env.VITE_DEMO_TAG) {
document.documentElement.classList.add('kiosk')
}
// Create Pinia store
const pinia = createPinia()

View file

@ -505,31 +505,6 @@ export async function initializeLightningServices(options?: {
}
console.log('[Lightning] LNbits wallet:', lnbitsWalletId)
// ── Public web demo: stamp the throwaway account so it can be swept ──────
// The browser demo (atm.demo.aiolabs.dev) runs with an EPHEMERAL identity —
// a fresh keypair per page load — so LNbits mints a new account + a fresh
// auto-credited wallet for every visitor. That isolation is the point (a
// single baked-in key would be credited exactly once and then drain), but it
// leaves throwaway accounts behind, and nothing in an auto-created row says
// "demo": pubkey-set/prvkey-NULL also describes a real ATM.
//
// A nostr pubkey can't carry a marker (you'd have to grind a vanity prefix,
// far too slow to do on page load), and the account/wallet the server
// auto-creates isn't nameable by the client. So we mint one extra,
// never-used wallet whose NAME is the tag: sweeping is then an exact string
// match on wallet name rather than a heuristic about what looks disposable.
//
// Unset on every real machine, so this is inert outside the demo build. The
// call is fire-and-forget: losing the marker degrades cleanup, not the demo.
const demoTag = (import.meta.env.VITE_DEMO_TAG as string | undefined)?.trim()
if (demoTag) {
void lnbits
.createWallet(demoTag)
// Never log the reply — create_wallet returns adminkey/inkey.
.then(() => console.log('[Lightning] Demo marker wallet created:', demoTag))
.catch((e) => console.warn('[Lightning] Demo marker wallet failed:', e))
}
// #70 P1: pull operator pubkey + fee config from LNbits over the authenticated
// transport (spirekeeper#41 `get_machine_config`). A seed-only machine has no
// VITE_OPERATOR_PUBKEYS, so without this it can't trust its fee config and sits

View file

@ -1,13 +1,10 @@
@import 'tailwindcss';
@import 'tw-animate-css';
/* Hide cursor completely on touchscreen kiosk.
Scoped to .kiosk (set on <html> by main.ts) so the public web demo, which
runs in an ordinary browser with a mouse, keeps a visible pointer. */
.kiosk,
.kiosk *,
.kiosk *::before,
.kiosk *::after {
/* Hide cursor completely on touchscreen kiosk */
*,
*::before,
*::after {
cursor: none !important;
}

View file

@ -33,7 +33,7 @@ esac
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
echo "=== Building bitSpire ATM Live USB ISO (model: $MODEL) ==="
echo "=== Building Lamassu ATM Live USB ISO (model: $MODEL) ==="
echo ""
echo "This is a pure Nix build — no local pnpm required."
echo ""

View file

@ -94,8 +94,7 @@
# pcscd gates client access via polkit; without a rule the sandboxed
# `bitspire` service user is "Rejected unauthorized PC/SC client". Authorize
# it to talk to the daemon and the card. The second rule lets the app trigger
# the NFC reader wedge-recovery service (see nfc-reader-reset below).
# it to talk to the daemon and the card.
security.polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
if ((action.id == "org.debian.pcsc-lite.access_pcsc" ||
@ -104,47 +103,8 @@
return polkit.Result.YES;
}
});
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
action.lookup("unit") == "nfc-reader-reset.service" &&
subject.user == "bitspire") {
return polkit.Result.YES;
}
});
'';
# NFC reader wedge-recovery. The Feitian R502-CL CCID reader (and, less often,
# any CCID reader) can wedge: it keeps detecting a card but every APDU returns
# "card absent or mute", and ONLY a USB power-cycle clears it — restarting
# pcscd or the app does not. This oneshot re-binds the reader's USB device (a
# software replug); pcscd + nfc-pcsc then re-detect it on hotplug with no app
# restart (verified on-device). The app (unprivileged `bitspire`) starts it via
# the polkit rule above when it sees repeated read failures. Reader-agnostic:
# it matches the USB CCID interface class (0x0B), so it also covers a future
# ACR1252U swap without a config change.
systemd.services.nfc-reader-reset = {
description = "Power-cycle a wedged CCID NFC reader (USB re-bind)";
serviceConfig = {
Type = "oneshot";
ExecStart = pkgs.writeShellScript "reset-nfc-reader" ''
set -u
found=0
for iface in /sys/bus/usb/devices/*:*/bInterfaceClass; do
[ -f "$iface" ] || continue
[ "$(${pkgs.coreutils}/bin/cat "$iface" 2>/dev/null)" = "0b" ] || continue
ifname=$(${pkgs.coreutils}/bin/basename "$(${pkgs.coreutils}/bin/dirname "$iface")")
dev=''${ifname%%:*}
echo "reset-nfc-reader: power-cycling CCID reader USB device $dev" >&2
echo -n "$dev" > /sys/bus/usb/drivers/usb/unbind 2>/dev/null || true
${pkgs.coreutils}/bin/sleep 2
echo -n "$dev" > /sys/bus/usb/drivers/usb/bind 2>/dev/null || true
found=1
done
[ "$found" = 1 ] || { echo "reset-nfc-reader: no CCID reader found" >&2; exit 1; }
'';
};
};
# Disable suspend/hibernate for kiosk
systemd.targets = {
sleep.enable = false;

View file

@ -1,4 +1,4 @@
# bitSpire ATM Live USB Configuration
# Lamassu ATM Live USB Configuration
# Bootable ISO for testing on physical hardware without installing to disk.
#
# Parameterized by machineModel (passed via specialArgs from flake.nix):

View file

@ -1,4 +1,4 @@
# bitSpire ATM Hardware udev Rules
# Lamassu ATM Hardware udev Rules
# Place in /etc/udev/rules.d/ or use services.udev.extraRules in NixOS
# ============================================

View file

@ -1,5 +1,5 @@
{
description = "bitSpire - Nostr-Native Lightning ATM";
description = "Lamassu Next - Nostr-Native Lightning ATM";
inputs = {
# Stable NixOS for the ATM OS base

View file

@ -1,4 +1,4 @@
# Pure Nix derivation for the bitSpire ATM Electron app.
# Pure Nix derivation for the Lamassu ATM Electron app.
#
# Uses fetchPnpmDeps + pnpmConfigHook to build entirely inside the Nix sandbox,
# eliminating the need for --impure or a local pnpm install.

View file

@ -7,7 +7,6 @@
"scripts": {
"dev": "turbo dev",
"build": "turbo build",
"build:web": "turbo build:web",
"test": "turbo test",
"lint": "turbo lint",
"format": "prettier --write .",

View file

@ -1,7 +1,7 @@
{
"name": "@bitSpire/hal",
"version": "0.1.0",
"description": "Hardware Abstraction Layer for bitSpire ATM devices",
"description": "Hardware Abstraction Layer for Lamassu ATM devices",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@ -44,7 +44,7 @@
"src"
],
"keywords": [
"bitspire",
"lamassu",
"atm",
"hardware",
"bill-validator",

View file

@ -1,7 +1,7 @@
/**
* @bitSpire/hal - Hardware Abstraction Layer
*
* Provides drivers for bitSpire ATM hardware devices:
* Provides drivers for Lamassu ATM hardware devices:
* - Bill validators (JCM iVIZION via ID003 protocol)
* - Bill dispensers (Fujitsu F53/F56)
*

View file

@ -37,7 +37,6 @@ import type {
CreateInvoiceBody,
PayInvoiceBody,
WalletInfo,
CreatedWallet,
MachineConfigResponse,
SubscribePaymentsBody,
SubscribeAck,
@ -212,17 +211,6 @@ export class LnbitsClient {
return data ?? []
}
/**
* Create an additional wallet on the calling account (`create_wallet`).
*
* Account-scoped (AUTH_ACCOUNT): the envelope carries no `wallet_id`, which
* is what makes the server resolve auth to the Account rather than a Wallet.
* NOT wrapped in `idempotent()` — a retry would mint a duplicate wallet.
*/
async createWallet(name: string): Promise<CreatedWallet> {
return this.sendRpc<CreatedWallet>('create_wallet', { body: { name } })
}
/** Pull server-delivered machine config (operator pubkey + fee config) over
* the authenticated transport — spirekeeper's `get_machine_config` RPC
* (bitspire#70 P1). Lets a seed-only ATM configure itself with no per-machine

View file

@ -66,7 +66,6 @@ export type {
CreateInvoiceBody,
PayInvoiceBody,
WalletInfo,
CreatedWallet,
SubscribePaymentsBody,
SubscribeAck,
SubscribePush,

View file

@ -112,15 +112,6 @@ export interface WalletInfo {
balance: number
}
/** Reply shape of the `create_wallet` RPC — unlike WalletInfo it carries the
* fresh wallet's keys, so never log it verbatim. */
export interface CreatedWallet {
id: string
name: string
adminkey: string
inkey: string
}
// ============================================================================
// Subscriptions
// ============================================================================

View file

@ -1,7 +1,7 @@
{
"name": "@bitSpire/nostr-client",
"version": "0.1.0",
"description": "Nostr client library for bitSpire ATM",
"description": "Nostr client library for Lamassu ATM",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View file

@ -1,5 +1,5 @@
/**
* Nostr client for bitSpire ATM
* Nostr client for Lamassu ATM
*
* Manages connections to Nostr relays with support for:
* - NIP-42 authentication

View file

@ -1,5 +1,5 @@
/**
* Event creation utilities for bitSpire ATM
* Event creation utilities for Lamassu ATM
*/
import { type Event, type EventTemplate, type VerifiedEvent, getEventHash } from 'nostr-tools'

View file

@ -1,7 +1,7 @@
/**
* @bitSpire/nostr-client
*
* Nostr client library for bitSpire ATM communication.
* Nostr client library for Lamassu ATM communication.
*
* Features:
* - NIP-42 authentication for private relays

View file

@ -1,5 +1,5 @@
/**
* Nostr client type definitions for bitSpire ATM
* Nostr client type definitions for Lamassu ATM
*/
import type { Event } from 'nostr-tools'

View file

@ -1,7 +1,7 @@
{
"name": "@bitSpire/ui-shared",
"version": "0.1.0",
"description": "Shared Vue 3 components for bitSpire ATM and dashboard",
"description": "Shared Vue 3 components for Lamassu ATM and dashboard",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View file

@ -1,7 +1,7 @@
/**
* @bitSpire/ui-shared
*
* Shared Vue 3 components for bitSpire ATM and dashboard.
* Shared Vue 3 components for Lamassu ATM and dashboard.
* This package will contain common UI components like:
* - QR code display
* - Number pad

View file

@ -5,11 +5,6 @@
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"build:web": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"env": ["VITE_*"]
},
"dev": {
"cache": false,
"persistent": true