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

lucifer-gate

v0.5.3

Published

AI agent command firewall with Telegram-based human approval

Readme

Lucifer Gate

AI agent command firewall with Telegram-based human approval.

Lucifer sits between your AI agent and the shell. It authenticates callers via API keys, matches commands against a policy file, and gates unknown commands through Telegram for human approval. Approved commands build up a permission library over time. Think "sudo via Telegram."

Quick start (2 minutes, no Telegram needed)

# Generate config files + API key
npx lucifer-gate --init .

# Start in dev mode (auto-approves everything, no Telegram)
LUCIFER_TELEGRAM_TOKEN=skip npx lucifer-gate --config ./config/lucifer.json --auto-approve

# In another terminal, test it:
curl -X POST http://localhost:3001/api/v1/execute \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_KEY_FROM_INIT" \
  -d '{"command":"echo hello"}'

How it works

  1. Caller sends POST /api/v1/execute with API key + command
  2. Lucifer authenticates the key and checks IP allowlist
  3. Command is matched against config/command-rules.json:
    • always_approve -> execute immediately
    • always_deny -> reject (403)
    • manual_approve -> check SQLite for cached approval, or send to Telegram
  4. If Telegram approval needed, human sees the command with risk warnings and taps a button
  5. Approved commands execute and results are returned to the caller

Config files

Generated by --init, hand-editable:

config/lucifer.json - Server settings (port, timeouts, limits, log file path)

config/api-keys.json - API key definitions (hashed, with optional IP allowlists)

config/command-rules.json - Command policy:

{
  "rules": [
    { "prefix": "echo ", "action": "always_approve" },
    { "prefix": "git pull", "action": "manual_approve" },
    { "prefix": "rm ", "action": "always_deny" }
  ],
  "defaultAction": "always_deny"
}

Rules matched top-to-bottom, first match wins.

Production setup (with Telegram)

  1. Create a Telegram bot via @BotFather and get the token
  2. Send any message to your bot from the chat you want to use for approvals
  3. Run the pairing command:
LUCIFER_TELEGRAM_TOKEN=your_bot_token npx lucifer-gate pair --config ./config/lucifer.json

The pairing wizard will:

  • Validate your bot token
  • List all chats that have messaged the bot (most recent first)
  • Let you pick which chat to use for approvals
  • Send a 6-digit verification code to that chat
  • Ask you to enter the code to prove you can read the chat
  • Save the chat ID to config/lucifer.json
  1. Start the server (no LUCIFER_TELEGRAM_CHAT_ID env var needed — it's in the config):
LUCIFER_TELEGRAM_TOKEN=your_bot_token npx lucifer-gate --config ./config/lucifer.json

Manual alternative: You can still set LUCIFER_TELEGRAM_CHAT_ID as an environment variable instead of using pair. The env var takes precedence over the config file value.

When a manual_approve command arrives, you'll see an inline keyboard in Telegram:

Command Request from key-name (192.168.1.5)
git push origin main

[Exact 2h] [Exact 8h] [Exact forever]
["git push" 2h] ["git push" 8h]
[Deny]

API

POST /api/v1/execute

Execute a command. The endpoint is synchronous: it blocks until the command reaches a terminal state (approved + executed, denied, or approval timed out) and returns the full result in a single response.

curl -X POST http://localhost:3001/api/v1/execute \
  -H "Content-Type: application/json" \
  -H "x-api-key: luc_yourkey" \
  -d '{"command":"git status"}'

Success response:

{ "requestId": "uuid", "status": "completed", "exitCode": 0, "stdout": "...", "stderr": "", "durationMs": 42 }

Status values observable on success/failure paths: completed, failed, denied, timed_out.

Error responses

All errors return:

{ "code": "ERROR_CODE", "message": "Human readable", "retryable": true }

Codes: MISSING_API_KEY, INVALID_API_KEY, IP_NOT_ALLOWED, RATE_LIMITED, COMMAND_DENIED, COMMAND_TOO_LONG, INVALID_CWD, DENIED, DUPLICATE_IN_FLIGHT (an identical command from this API key is already awaiting approval — retry after it settles), APPROVAL_TIMEOUT, APPROVAL_ERROR.

CLI

lucifer-gate --init [dir]              Generate starter config + API key
lucifer-gate pair [--config <path>]    Pair a Telegram chat for approvals
lucifer-gate --config <path>           Start server with config
lucifer-gate --auto-approve            Dev mode (no Telegram)
lucifer-gate log [--limit N]           Query audit log
lucifer-gate stats                     Show approval statistics

Docker

docker build -t lucifer-gate .
docker run -p 3001:3001 \
  -e LUCIFER_TELEGRAM_TOKEN=your_token \
  -e LUCIFER_TELEGRAM_CHAT_ID=your_chat_id \
  -v ./config:/app/config \
  -v ./data:/app/data \
  lucifer-gate

Logging

Lucifer logs to both console and file by default.

Console output is human-readable (colorized, formatted) when pino-pretty is available, and falls back to structured JSON otherwise. pino-pretty is included when you clone the repo and run npm install, but is not bundled with the published npm package — so npx lucifer-gate uses JSON console output. This is expected and fully functional.

File output is always structured JSON (one object per line), written to data/lucifer.log by default. Each line is a complete JSON object, making it easy to search, filter, and feed into log aggregators.

File logging is enabled via config/lucifer.json:

{
  "logFile": "lucifer.log",
  ...
}

The logFile path is relative to dataDir (default: data/lucifer.log). Remove the key to disable file logging. The --init command generates the config with file logging enabled.

Log levels (controlled via LOG_LEVEL env var):

  • debug — verbose output (default in development)
  • info — normal operations (default in production, when NODE_ENV=production)
  • warn — potential issues
  • error — failures only

Example:

LOG_LEVEL=info LUCIFER_TELEGRAM_TOKEN=your_token npx lucifer-gate --config ./config/lucifer.json

Environment variables

| Variable | Required | Description | |---|---|---| | LUCIFER_TELEGRAM_TOKEN | Yes (prod) | Telegram bot token from @BotFather | | LUCIFER_TELEGRAM_CHAT_ID | No | Telegram chat ID (use pair command instead, or set manually) | | LUCIFER_ADMIN_SECRET | No | Bearer token for admin endpoints | | PORT | No | Server port (default: 3001) | | LOG_LEVEL | No | Log level: debug, info, warn, error (default: debug / info in production) | | NODE_ENV | No | Set to production for production defaults (info log level, no pretty-printing) |

Architecture

server/src/domains/
  command-gateway/       Core domain
    types/               CommandRequest, Approval, types
    config/              Gateway configuration
    repository/          SQLite stores, JSON config readers
    service/             Auth, rules, risk analysis, execution, approvals
    api/                 Execute routes + web approval UI routes
  platform-api/          Health endpoint + server runtime wiring

Dependency flow: Types -> Config -> Repository -> Service -> Runtime -> UI/API

Stack

  • Express 5 + TypeScript
  • SQLite (better-sqlite3) for approvals + audit
  • Telegraf for Telegram bot
  • Optional server-delivered web approval UI with SSE updates
  • Pino for structured logging (pino-pretty for human-readable console output in dev)
  • Vitest for testing