npm package: @flarcos/kifira-lite
Description: A lightweight Open Payments gateway — no Rafiki setup required
Author: flarcos
Latest version: 0.1.0

# Kifira Lite

> A lightweight, developer-friendly Open Payments gateway. No Docker, no Kubernetes, no Rafiki setup — just `npx` and you're running.

[![Open Payments](https://img.shields.io/badge/Open_Payments-compatible-8b5cf6)](https://openpayments.dev)
[![GNAP](https://img.shields.io/badge/GNAP-RFC_9635-6366f1)](https://www.rfc-editor.org/rfc/rfc9635.html)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

## What is this?

Kifira Lite implements the [Open Payments](https://openpayments.dev) API surface — the same REST endpoints that [Rafiki](https://rafiki.dev) exposes — as a single lightweight Node.js process. It's designed for:

- **SDK Testing** — Point your Open Payments client at `localhost:3000` instead of deploying a full Rafiki stack
- **Arazzo Workflow Validation** — Run machine-readable workflow specs against a real server
- **Prototyping** — Build payment UIs without infrastructure overhead
- **Learning** — Understand Open Payments by reading ~1200 lines of TypeScript

## Quick Start

```bash
npx @flarcos/kifira-lite --wallet-address http://localhost:3000/alice --generate-key
```

That's it. Two servers start:

- **Resource Server** at `http://localhost:3000` — wallet address metadata, payments, quotes
- **Auth Server** at `http://localhost:3001` — GNAP grants, tokens, consent UI

## Installation

```bash
npm install @flarcos/kifira-lite
```

### As a Library

```typescript
import { createKifiraServer, generateServerKey, StubBackend } from '@flarcos/kifira-lite';
import { serve } from '@hono/node-server';

const key = await generateServerKey();

const { resourceApp, authApp } = await createKifiraServer({
  walletAddress: 'http://localhost:3000/alice',
  publicName: 'Alice',
  assetCode: 'USD',
  assetScale: 2,
  port: 3000,
  authPort: 3001,
  storage: 'memory',
  privateKey: key.pem,
  paymentBackend: new StubBackend(),
});

serve({ fetch: resourceApp.fetch, port: 3000 });
serve({ fetch: authApp.fetch, port: 3001 });
```

## API Surface

### Wallet Address Server (public)

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/alice` | Wallet address metadata |
| `GET` | `/alice/jwks.json` | Server public keys (JWK Set) |

### Auth Server (GNAP / RFC 9635)

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/` | Request a grant |
| `POST` | `/continue/:id` | Continue a grant |
| `DELETE` | `/continue/:id` | Cancel a grant |
| `POST` | `/token/:id` | Rotate an access token |
| `DELETE` | `/token/:id` | Revoke an access token |
| `GET` | `/interact/:id` | Consent UI (interactive grants) |

### Resource Server (token-protected)

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/alice/incoming-payments` | Create incoming payment |
| `GET` | `/alice/incoming-payments` | List incoming payments |
| `GET` | `/alice/incoming-payments/:id` | Get incoming payment |
| `POST` | `/alice/incoming-payments/:id/complete` | Complete incoming payment |
| `POST` | `/alice/quotes` | Create quote |
| `GET` | `/alice/quotes/:id` | Get quote |
| `POST` | `/alice/outgoing-payments` | Create outgoing payment |
| `GET` | `/alice/outgoing-payments` | List outgoing payments |
| `GET` | `/alice/outgoing-payments/:id` | Get outgoing payment |

## Usage Examples

### 1. Discover a Wallet Address

```bash
curl http://localhost:3000/alice
```

```json
{
  "id": "http://localhost:3000/alice",
  "publicName": "Alice",
  "assetCode": "USD",
  "assetScale": 2,
  "authServer": "http://localhost:3001",
  "resourceServer": "http://localhost:3000/alice"
}
```

### 2. Get a Token (Non-Interactive)

```bash
curl -X POST http://localhost:3001/ \
  -H "Content-Type: application/json" \
  -d '{
    "access_token": {
      "access": [{ "type": "incoming-payment", "actions": ["create", "read"] }]
    },
    "client": "http://localhost:3000/alice"
  }'
```

### 3. Create an Incoming Payment

```bash
curl -X POST http://localhost:3000/alice/incoming-payments \
  -H "Content-Type: application/json" \
  -H "Authorization: GNAP <token>" \
  -d '{
    "walletAddress": "http://localhost:3000/alice",
    "incomingAmount": { "value": "1000", "assetCode": "USD", "assetScale": 2 }
  }'
```

### 4. Interactive Grant (Outgoing Payment)

Outgoing payments require user consent. The flow is:

```
Client → POST /         (grant request)
         ← { interact: { redirect: "/interact/:id" } }

User   → GET /interact/:id  (consent page rendered)
         → POST /interact/:id/approve
         ← 302 → client callback with interact_ref

Client → POST /continue/:id  (with interact_ref)
         ← { access_token: { value: "..." } }
```

## Architecture

```
┌─────────────────────────┐     ┌─────────────────────────┐
│   Resource Server :3000 │     │    Auth Server :3001     │
│                         │     │                         │
│  GET  /alice            │     │  POST /         (grant) │
│  GET  /alice/jwks.json  │     │  POST /continue/:id     │
│  POST /alice/incoming-* │◄────│  POST /token/:id        │
│  POST /alice/quotes     │     │  GET  /interact/:id     │
│  POST /alice/outgoing-* │     │                         │
└────────┬────────────────┘     └─────────────────────────┘
         │
    ┌────▼────┐
    │ Storage │  (Memory or SQLite)
    └────┬────┘
         │
  ┌──────▼───────┐
  │   Payment    │  (StubBackend or custom)
  │   Backend    │
  └──────────────┘
```

## Configuration

### CLI Options

```
--wallet-address <url>     Wallet address URL (required)
--name <name>              Display name (default: from URL)
--asset-code <code>        Currency code (default: USD)
--asset-scale <n>          Decimal scale (default: 2)
--port <n>                 Resource server port (default: 3000)
--auth-port <n>            Auth server port (default: 3001)
--storage <type>           "memory" or "sqlite" (default: memory)
--sqlite-path <path>       SQLite file path (default: ./kifira.db)
--key <path>               Ed25519 private key PEM file
--generate-key             Generate a new key pair on startup
```

### Storage

- **Memory** (default) — Zero config, data lost on restart. Perfect for testing.
- **SQLite** — Persistent, WAL-mode, auto-creates tables. Use `--storage sqlite`.

### Payment Backend

The `PaymentBackend` interface abstracts money movement. Kifira Lite ships with a `StubBackend` that auto-succeeds all payments (great for testing). You can implement your own:

```typescript
import type { PaymentBackend } from '@flarcos/kifira-lite';

class StripeBackend implements PaymentBackend {
  async getQuote(params) {
    // Call Stripe API for exchange rate
    return { debitAmount, receiveAmount, expiresAt };
  }

  async executePayment(params) {
    // Call Stripe transfer API
    return { success: true, sentAmount };
  }
}
```

## How it Compares to Rafiki

| Feature | Rafiki | Kifira Lite |
|---------|--------|-------------|
| Setup time | Hours (Docker, Postgres, Redis) | Seconds (`npx`) |
| Storage | PostgreSQL + Redis | In-Memory or SQLite |
| ILP Connector | Full STREAM/ILP | Stub (wire-compatible URLs) |
| GNAP Auth | Full RFC 9635 | Full RFC 9635 |
| HTTP Signatures | RFC 9421 verification | RFC 9421 (configurable) |
| Consent UI | Separate frontend | Built-in dark-mode page |
| Production ready | ✅ | ❌ (designed for dev/test) |

## Development

```bash
# Install dependencies
npm install

# Run tests
npm test

# Start dev server
npx tsx src/cli.ts --wallet-address http://localhost:3000/alice --generate-key
```

## Testing

54 tests covering:
- **Wallet address** — metadata, JWKS endpoints
- **GNAP auth** — non-interactive grants, interactive consent flow, token rotation/revocation
- **Resources** — incoming payments CRUD, quotes, outgoing payments
- **Storage** — both Memory and SQLite adapters
- **E2E** — full one-time payment workflow over HTTP

```bash
npm test
```

## License

Apache-2.0