Skip to content

Commit 69e32bf

Browse files
authored
Merge pull request #171 from PortalTechnologiesInc/wheatley/rest-api-webhooks
refactor(portal-rest): REST + polling + webhooks API
2 parents 83ab477 + e5b935f commit 69e32bf

66 files changed

Lines changed: 5731 additions & 7363 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ node_modules
99

1010
react-native/portal-app-lib-*.tgz
1111

12-
result
12+
result
13+
examples/.env

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1212

1313
---
1414

15+
### [0.4.0] - 2026-03-17
16+
17+
#### Changed
18+
- **WebSocket → REST**: All endpoints are now standard HTTP (POST/GET/PUT/DELETE); WebSocket API removed
19+
- Async operations (key handshake, payments, invoices, cashu) now return `stream_id` immediately; events are delivered via webhooks or polling (`GET /events/:stream_id`)
20+
- `GET /version` response now wrapped in standard `ApiResponse` envelope
21+
- Profile is now set via `[profile]` config / env vars at startup; `SetProfile` command removed
22+
- `FAKE_PAYMENTS` feature gated behind `#[cfg(debug_assertions)]`
23+
- `/.well-known/nostr.json` is now a public endpoint (no auth required) for NIP-05 verification
24+
25+
#### Added
26+
- **Webhook delivery**: HMAC-SHA256 signed POST (`X-Portal-Signature` header) to configured URL on every stream event
27+
- **SQLite event persistence**: Events stored with WAL mode; startup recovery for in-flight `single_payment` streams
28+
- `GET /info`: New authenticated endpoint returning server public key
29+
- **TypeScript SDK**: `autoPollingIntervalMs` config option starts a background scheduler; `poll(op)` now typed — takes `AsyncOperation<T>`, returns `T`; `destroy()` stops the auto-poller; `webhookHandler()` for HMAC-verified webhook processing
30+
- **Java SDK** (`portal-java-sdk`): Full rewrite from WebSocket to REST; `PortalClient` with `PortalClientConfig` supporting manual polling, auto-polling, and webhooks; all async methods return typed `AsyncOperation<T>`
31+
- **OpenAPI spec**: `openapi.yaml` with all endpoints, request/response schemas, and `StreamIdResponse` for async operations
32+
- **Documentation**: Full mdBook docs with HTTP/curl tabs on every page, REST API guide, interactive OpenAPI viewer (Redoc), updated environment variables reference
33+
- **Examples**: Runnable Node.js examples (`auth.js`, `single-payment.js`, `profile.js`, `error-handling.js`) with `.env` support
34+
- **Security**: Constant-time Bearer token comparison (`subtle::ConstantTimeEq`); constant-time webhook signature verification (`timingSafeEqual`)
35+
- **Performance**: Shared `reqwest::Client` in `EventStore` for webhook delivery (no per-event allocation)
36+
37+
#### Fixed
38+
- TypeScript SDK: `requestSinglePayment`, `requestPaymentRaw`, `requestRecurringPayment` now return correctly typed `AsyncOperation<T>` (previously returned raw `StreamEvent`)
39+
40+
#### Removed
41+
- WebSocket API (`/ws` endpoint)
42+
- `SetProfile` / `ListenClosedRecurringPayment` commands
43+
44+
---
45+
1546
### [0.3.0] - 2026-02-26
1647

1748
#### Changed

Cargo.lock

Lines changed: 7 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ bitcoin = "0.32.5"
3838
jwt-compact = { version = "0.9.0-beta.1", features = ["es256k"] }
3939
secp256k1 = "0.29"
4040
sha2 = "0.10"
41+
hmac = "0.12"
4142

4243
# -----------------------------------------------------------------------------
4344
# Error handling & logging
@@ -87,6 +88,7 @@ uniffi = "0.29.1"
8788
# -----------------------------------------------------------------------------
8889
config = { version = "0.15.19", features = ["toml"] }
8990
dashmap = "6.1.0"
91+
rusqlite = { version = "0.31", features = ["bundled"] }
9092
dirs = "6.0.0"
9193

9294
# -----------------------------------------------------------------------------

README.md

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77

88
**Portal is the identity and payment layer for Bitcoin-native applications — no accounts, no KYC, no payment processor.**
99

10-
Users interact through the Portal mobile app using their Nostr identity. You run a single Docker container and talk to it with our SDK.
10+
Users interact through the Portal mobile app using their Nostr identity. You run a single Docker container and call its REST API from any language.
1111

1212
---
1313

1414
## How it works
1515

1616
```
17-
Your backend ←—WebSocket—→ sdk-daemon ←—Nostr relays—→ Portal app (user's phone)
17+
Your backend ←—REST API—→ sdk-daemon ←—Nostr relays—→ Portal app (user's phone)
1818
```
1919

2020
1. Your backend asks sdk-daemon for a handshake URL → show it as a QR code
@@ -31,46 +31,26 @@ Your backend ←—WebSocket—→ sdk-daemon ←—Nostr relays—→ Porta
3131
docker run -d -p 3000:3000 \
3232
-e PORTAL__AUTH__AUTH_TOKEN=your-secret-token \
3333
-e PORTAL__NOSTR__PRIVATE_KEY=your-nostr-private-key-hex \
34-
getportal/sdk-daemon:0.3.0
34+
getportal/sdk-daemon:0.4.0
3535
```
3636

37-
**2. Install the SDK**
37+
**2. Call the REST API**
3838

3939
```bash
40-
npm install portal-sdk # TypeScript / JavaScript
41-
```
42-
43-
```groovy
44-
// Java (Gradle) — via JitPack
45-
implementation 'com.github.PortalTechnologiesInc:java-sdk:0.3.0'
46-
```
47-
48-
**3. Connect and authenticate a user**
40+
# Get a handshake URL — show it to the user as a QR code
41+
curl -s -X POST http://localhost:3000/key-handshake \
42+
-H "Authorization: Bearer your-secret-token" \
43+
-H "Content-Type: application/json" \
44+
-d '{}' | jq .
4945

50-
```typescript
51-
import { PortalSDK } from 'portal-sdk';
46+
# → { "stream_id": "abc123", "url": "nostr+walletconnect://..." }
5247

53-
const client = new PortalSDK({ serverUrl: 'ws://localhost:3000/ws' });
54-
await client.connect();
55-
await client.authenticate('your-secret-token');
56-
57-
const url = await client.newKeyHandshakeUrl((userKey) => {
58-
console.log('User authenticated:', userKey);
59-
});
60-
// Show `url` as a QR code to the user
48+
# Poll for the user's public key (repeat until events arrive)
49+
curl -s "http://localhost:3000/events/abc123?after=0" \
50+
-H "Authorization: Bearer your-secret-token" | jq .
6151
```
6252

63-
---
64-
65-
## SDK versions
66-
67-
| SDK | Version | Install |
68-
|-----|---------|---------|
69-
| TypeScript / JavaScript | `0.3.0` | `npm install portal-sdk` |
70-
| Java | `0.3.0` | [JitPack](https://jitpack.io/#PortalTechnologiesInc/java-sdk) |
71-
| Docker image | `0.3.0` | `docker pull getportal/sdk-daemon:0.3.0` |
72-
73-
The SDK `major.minor` version must match the daemon. Patch versions are independent. See [Versioning & Compatibility](https://portaltechnologiesinc.github.io/lib/getting-started/versioning.html).
53+
Any language works — Python, Go, Ruby, PHP, Java, TypeScript. No SDK required.
7454

7555
---
7656

@@ -84,11 +64,30 @@ The SDK `major.minor` version must match the daemon. Patch versions are independ
8464

8565
---
8666

67+
## Documentation
68+
69+
Full docs at **[portaltechnologiesinc.github.io/lib](https://portaltechnologiesinc.github.io/lib/)** — REST API reference, curl examples, guides for authentication, payments, Cashu, JWT, Docker deployment, and more.
70+
71+
---
72+
73+
## Official SDKs
74+
75+
For JavaScript/TypeScript and Java, typed SDKs are available. They wrap the same REST API with auto-polling, webhook handling, and typed responses.
76+
77+
| SDK | Version | Install |
78+
|-----|---------|---------|
79+
| TypeScript / JavaScript | `0.4.0` | `npm install portal-sdk` |
80+
| Java | `0.4.0` | [JitPack](https://jitpack.io/#PortalTechnologiesInc/java-sdk) |
81+
82+
The SDK `major.minor` version must match the daemon. See [Versioning & Compatibility](https://portaltechnologiesinc.github.io/lib/getting-started/versioning.html).
83+
84+
---
85+
8786
## This repository
8887

8988
| Package | Description |
9089
|---------|-------------|
91-
| `portal-rest` | SDK Daemon — HTTP + WebSocket API server |
90+
| `portal-rest` | SDK Daemon — REST API server |
9291
| `portal` | Core Nostr protocol and conversation logic |
9392
| `portal-app` | App runtime and wallet integration |
9493
| `portal-wallet` | Wallet backends (NWC, Breez) |
@@ -98,10 +97,4 @@ The SDK `major.minor` version must match the daemon. Patch versions are independ
9897

9998
---
10099

101-
## Documentation
102-
103-
Full docs at **[portaltechnologiesinc.github.io/lib](https://portaltechnologiesinc.github.io/lib/)** — guides for authentication, payments, Cashu, JWT, relay setup, Docker deployment, and more.
104-
105-
---
106-
107100
**License:** MIT · [Java SDK](https://github.com/PortalTechnologiesInc/java-sdk) · [Demo](https://github.com/PortalTechnologiesInc/portal-demo)

backend/.envrc

Lines changed: 0 additions & 1 deletion
This file was deleted.

backend/.gitignore

Lines changed: 0 additions & 7 deletions
This file was deleted.

backend/flake.lock

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)