npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@riposte.co/server

v0.4.6

Published

<p align="center"> <img src="assets/riposte-text.svg" alt="Riposte logo" width="280"> </p>

Downloads

220

Readme

Riposte

On-premise email sync service - Cloud-API alternative

Riposte provides a unified API for multiple email providers (Gmail, Microsoft, future IMAP) with 90% cost savings compared to Cloud-APIs. All data stays on your infrastructure.

🚀 Quick Start

# Scaffold your configuration (riposte/sync.config.json + riposte/.env.example)
npx riposte-init

# Launch the daemon with the admin portal
npx riposte-run

# Skip the admin portal
npx riposte-run --no-admin

That's it! Your email sync service is running at:

  • API Server: http://localhost:8080
  • Admin Portal: Auto-selected port printed on startup (for example http://localhost:3001)

Installation at a glance

Riposte follows a three-step flow so you always know what comes next:

  1. Initialize – Run npx riposte-init to auto-detect local services, scaffold riposte/sync.config.json/.env.example, and hand off deeper customization to the admin onboarding wizard.
  2. Launch – Start the runtime with npx riposte-run and watch the structured logs confirm database, queue, and migration health.
  3. Admin – Visit the onboarding flow at the admin portal URL shown after startup. It now behaves like a linear checklist: choose Google or Microsoft first, paste credentials with the exact scopes we flag, wire databases/queues, and (optionally) authorize a pilot account before copying the API call examples into your app.

Each stage is documented in the installation guide, which expands on prerequisites, troubleshooting tips, and day-two operations once you are live.

📦 Installation Options

What ships in the npm tarball?

Running npm publish --dry-run (or npm pack) lists every file that would be included in the release. You will see a number of entries under admin-dist/.next/standalone/node_modules/…. Those files come from the Next.js "standalone" build that powers the bundled admin portal. They are not a copy of your development node_modules; instead they are the trimmed runtime dependencies that Next.js inlines alongside server.js so the riposte-admin CLI can boot without relying on an external installation. The build script already deletes caches and other transient output, so the additional files are expected and safe to publish.

Option 1: Global Installation (Recommended)

npm install -g @riposte.co/server
riposte-init
riposte-run

Option 2: Local Installation

npm install @riposte.co/server
npx riposte-init
npx riposte-run

Option 3: Development Setup

git clone <private-repo>
cd riposte
npm install
npm run setup
npm start

⚙️ Configuration

Quick Start (SQLite + In-Memory)

  • Database: SQLite file (./riposte.db)
  • Cache: In-memory
  • Queues: In-memory
  • Perfect for: Trying out, development, small deployments

Production Setup (PostgreSQL + Redis)

  • Database: PostgreSQL
  • Cache: Redis
  • Queues: Redis
  • Perfect for: Production, horizontal scaling

Database Configuration & Migrations

  • Set DATABASE_URL (or TEST_DATABASE_URL) to file:./path/to.db or any sqlite: URL to run with SQLite.
  • Set the same variable to a PostgreSQL connection string (for example postgresql://user:pass@localhost:5432/riposte) to run with Postgres.
  • The service automatically creates SQLite database files when using file: URLs.
  • Generate a new migration with npm run db:generate <migration-name>. The command compares the current schema to your target database and writes the SQL files under drizzle/sqlite or drizzle/postgres as appropriate.
  • Run migrations with npm run db:migrate. The script picks the correct migration directory (drizzle/sqlite or drizzle/postgres) based on the active provider.
  • Generated migrations live under drizzle/sqlite or drizzle/postgres depending on the dialect selected when running drizzle-kit.
  • Riposte now auto-applies pending migrations during startup. Set AUTO_APPLY_MIGRATIONS=false (or riposte.migrations.autoApply to false in sync.config.json) if you prefer to run the migration script manually.

Authentication screen

Riposte now ships with a built-in authentication screen that runs alongside your deployment at /auth/authorize. The product creates short-lived sessions and returns links that you can hand to end users—no external services required. The screen detects the correct provider based on the email address, renders a consent experience, and drives the entire PKCE exchange on the server.

Terminology to avoid confusion:

  • Provider callback URI: The OAuth callback URL registered with Google or Microsoft that Riposte listens on to receive the authorization code (e.g., http://localhost:8080/providers/gmail/callback). Configure this under riposte.oauth.<provider>.redirectUri or the matching environment variable.
  • App redirect URL: The user-facing URL in your product where the browser should land after Riposte finishes authentication (e.g., https://app.example.com/connect/success). Configure this under riposte.app.redirectUrl or pass it when creating an auth session.

Once the provider completes the handshake, Riposte exchanges the code for tokens, associates the account with the originating session, and redirects the browser to your app redirect URL.

Customize the experience under riposte.authScreen inside riposte/sync.config.json. All of the values live in source control so you can review branding changes just like any other configuration update:

{
  "riposte": {
    "authScreen": {
      "enabled": true,
      "publicUrl": "https://mail.example.com",
      "brandName": "Acme Support",
      "logoText": "AS",
      "title": "Connect your email",
      "subtitle": "We will securely connect your inbox for {{brand}}.",
      "supportEmail": "[email protected]",
      "submitButtonLabel": "Continue",
      "customCssFile": "./branding/auth-screen.css",
      "cors": {
        "origin": "*",
      },
    },
  },
}

Point customCssFile at a stylesheet to control colors, fonts, or layout. The file is reloaded whenever it changes so you can iterate quickly in development. When enabled is set to false the server returns 503 for new session requests to make it clear that the experience has been turned off.

By default the screen is accessible from any origin. Set riposte.authScreen.cors.origin to a specific origin (or list of origins) if you need to lock it down for production embeds.

🧪 Live provider smoke tests

The repository includes a Vitest suite that exercises real providers end-to-end. Enable it locally by setting RUN_LIVE_PROVIDER_TESTS=true and pointing the harness at one or more seeded accounts via either PROVIDER_TEST_RUNS (JSON array) or the simpler PROVIDER_TEST_SENDER_ID/PROVIDER_TEST_RECIPIENT_IDS flags described in product/src/__tests__/integration/live-provider.integration.test.ts.

When calendar access is provisioned for the connected account the harness now validates basic calendar CRUD in addition to email send/receive. Specify PROVIDER_TEST_CALENDAR_ID to force the test to use a specific calendar; otherwise the first calendar returned by the provider will be used. Temporary events are deleted automatically even if an assertion fails.

🔐 Licensing

Riposte now enforces a license key for deployments that connect more than 100 accounts. Without a valid license the server will refuse new authorizations once 100 accounts are connected. A valid license lifts that limit (or applies a custom max if specified) and is verified with an Ed25519 signature at startup.

Configure license verification

Set the following environment variable (typically inside riposte/.env):

| Variable | Description | | --------------------- | -------------------------------------------- | | RIPOSTE_LICENSE_KEY | The signed license string issued by Riposte. |

The verifier ships with a baked-in Ed25519 public key (product/src/common/license-authority.ts). Riposte ignores runtime overrides unless the process runs under NODE_ENV=test so operators cannot self-issue licenses by swapping keys. Unit tests rely on that test-only escape hatch to inject ephemeral key pairs.

Note: If no valid license is configured the server logs a warning at startup and enforces the 100-account cap automatically.

Generate a signing key pair

Use the bundled helper to create an Ed25519 key pair for signing licenses:

npm run license -- keypair --output ./license-keys
# or omit --output to print keys to the console only

The command prints:

  • riposte-license-private-key.pem (PKCS8 private key for signing)
  • riposte-license-public-key.pem (SPKI public key)
  • Base64url versions of the public key for reference

After generating a new pair, update product/src/common/license-authority.ts and license/canonical-public-key.raw with the printed base64url raw public key. Distribute the corresponding private key securely for license issuance; it should never be committed to git.

Sign a license key

Create licenses with custom expirations and limits using the same helper:

npm run license -- sign \
  --expires 2025-12-31 \
  --max-accounts 1000 \
  --name "Acme Corp" \
  --id enterprise-2025 \
  --private-key-path ./license-keys/riposte-license-private-key.pem

Key details:

  • --expires accepts ISO-8601 dates or relative durations such as 180d or 2y.
  • --max-accounts is optional; omit it for unlimited accounts on that license.
  • --name and --id embed metadata that appears in server logs when the license is loaded.
  • Provide the private key via --private-key, --private-key-path, or the environment variables RIPOSTE_LICENSE_PRIVATE_KEY(_PATH).

The command prints the signed license payload and the final RIPOSTE_LICENSE_KEY value to distribute.

🔄 Migration

When you're ready to scale from SQLite to PostgreSQL:

riposte-migrate --to postgresql

This will:

  1. Back up your SQLite data
  2. Update configuration to PostgreSQL
  3. Restart the service
  4. Validate the migration

🏗️ Architecture

Core Components

  • API Server: Fastify-based REST API (port 8080)
  • Admin Portal: Next.js web interface served from its own HTTP listener (auto-selected port, printed on startup)
  • Database: SQLite (dev) or PostgreSQL (prod)
  • Cache/Queues: In-memory (dev) or Redis (prod)

Supported Providers

  • Gmail: Full sync, labels, drafts, send
  • Microsoft: Full sync, folders, drafts, send
  • IMAP: Mailbox sync and raw message access (bring your own IMAP server)

IMAP provider setup

IMAP support is designed for legacy mailboxes that expose username/password or app-specific password credentials. Because IMAP does not provide OAuth, credentials are stored as provider tokens. To enable IMAP:

  1. Install the optional runtime dependencies in the environment where Riposte runs:

    npm install imapflow mailparser
  2. Create an account in the ProviderAuthToken table (via the admin UI or directly through the database) using a JSON payload in the accessToken field. Example:

    {
      "host": "imap.example.com",
      "port": 993,
      "secure": true,
      "username": "[email protected]",
      "password": "app-specific-password",
      "mailbox": "INBOX"
    }

    Additional tls options (for example {"rejectUnauthorized": false}) can be provided if your server uses a self-signed certificate.

  3. Queue a historical sync or use the /sync endpoints to pull messages. IMAP support currently focuses on read-only synchronization and raw message retrieval. Sending, label management, and calendar/contacts are not yet implemented for IMAP accounts.

Key Features

  • Real-time sync: Push notifications + polling
  • Rate limiting: Global queue with provider limits
  • Horizontal scaling: Redis-based distributed system
  • Data sovereignty: Everything stays on-premise
  • Cost savings: 90% cheaper than Cloud-APIs

📊 Business Value

Cost Comparison

  • Cloud-APIs: $1.50 per connected account
  • Riposte: $99/month unlimited accounts
  • Savings: 90% cost reduction

Target Market

  • SMBs: Priced out of Cloud-APIs
  • Enterprises: Want cost savings + data control
  • Agencies: Managing multiple clients
  • Developers: Building CRMs, ATSs, etc.

🔧 Development

Prerequisites

  • Node.js 18+
  • npm or yarn
  • PostgreSQL (for production)
  • Redis (for production)
  • [Optional] better-sqlite3 when running against SQLite (install with npm install better-sqlite3)

Setup

# Clone the repository
git clone <private-repo>
cd riposte

# Install dependencies
npm install

# Set up development environment
npm run setup

# Start development server
npm run dev

Notifications

Each provider can use either webhook deliveries or polling to detect mailbox changes. Configure the preference in the notifications section of your sync.config.json (or via the corresponding environment variables). Gmail defaults to webhooks, while Microsoft starts in polling mode so the integration works without hosting a public webhook endpoint. Switch Microsoft to webhooks by setting notifications.providers.microsoft.mode to "webhook" once you have a reachable HTTPS callback.

The built-in polling engine runs automatically for every provider configured with mode: "polling". Tune its behaviour under notifications.polling:

{
  "notifications": {
    "polling": {
      "intervalMs": 60000, // How often Riposte polls each account
      "batchSize": 25, // Messages fetched per account during a cycle
      "initialLookbackMs": 900000, // Look back window for the very first poll (in ms)
      "maxAccountsPerCycle": 10, // Accounts processed per polling loop
    },
  },
}

Override the same values via environment variables:

NOTIFICATIONS_POLLING_INTERVAL_MS=60000
NOTIFICATIONS_POLLING_BATCH_SIZE=25
NOTIFICATIONS_POLLING_INITIAL_LOOKBACK_MS=900000
NOTIFICATIONS_POLLING_MAX_ACCOUNTS=10

Whenever a new account connects we enqueue an immediate polling run so you receive message.received events without waiting for the next interval.

Building for Production

# Build everything
npm run build

# Start production server
npm start

📝 API Documentation

Once running, visit:

  • Swagger UI: http://localhost:8080/docs
  • API Reference: http://localhost:8080/documentation/json

🛡️ Security

  • API Key Authentication: All endpoints require API key
  • Data Encryption: Sensitive data encrypted at rest
  • On-premise Only: No external data sharing
  • Token Management: Secure OAuth token storage

📈 Monitoring

  • Health Check: GET /healthz
  • Metrics: Built-in performance monitoring
  • Logs: Structured logging with Pino
  • Alerts: Configurable error notifications

🤝 Support

For support and questions:

  • Documentation: See /docs endpoint
  • Issues: Contact support team
  • Updates: npm update riposte

📄 License

UNLICENSED - Proprietary software


Riposte - The Cloud-API alternative that saves you 90% on email integration costs.