@monrow/sdk
v0.3.7
Published
AI cost protection that runs inside your app. Catch runaway loops before they bankrupt you.
Maintainers
Readme
@monrow/sdk
AI cost protection that runs inside your app. Wraps the Anthropic and/or OpenAI client, catches runaway loops and cost ceilings, throws an error before the next call fires. Optional cloud sync (Pro) streams cost-per-feature data to monrow.io.
- Zero external runtime dependencies. Optional peer deps:
@anthropic-ai/sdk,openai(install whichever you use). - TypeScript, Node 18+.
- Local append-only incident log at
.monrow/incidents.jsonl. - Local-only mode is free and unchanged. Cloud sync is opt-in via
apiKey.
Install
npm install @monrow/sdk
# install the provider SDK(s) you use:
npm install @anthropic-ai/sdk
npm install openaiWrap your Anthropic client
import Anthropic from '@anthropic-ai/sdk'
import {
Monrow,
MonrowAbuseError,
MonrowBlockedError,
MonrowCostLimitError,
MonrowPausedError,
} from '@monrow/sdk'
const monrow = new Monrow({
// optional: enable cloud sync for cost-per-feature dashboards
apiKey: process.env.MONROW_KEY,
})
const anthropic = monrow.wrap(
new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })
)OpenAI support
import { Monrow, MonrowAbuseError } from '@monrow/sdk'
import OpenAI from 'openai'
const monrow = new Monrow()
const openai = monrow.wrapOpenAI(
new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
)
async function handler() {
try {
const response = await openai.chat.completions.create({
model: 'gpt-4o',
monrow: { user: 'usr_bf29', feature: 'checkout-flow' },
messages: [{ role: 'user', content: 'hello' }],
})
return response
} catch (err) {
if (err instanceof MonrowAbuseError) {
// e.g. return a cached fallback instead of failing the request
return null
}
throw err
}
}Works exactly like the Anthropic wrapper. All detectors, incident logging, cost tracking, and cloud sync apply equally.
Use as normal
The wrapped client behaves identically. The only addition is an optional per-call attribution field:
try {
const res = await anthropic.messages.create({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
messages: [{ role: 'user', content: 'hello' }],
// @ts-expect-error — monrow extends the request
monrow: { user: 'usr_bf29', feature: 'checkout-flow' },
})
} catch (err) {
if (err instanceof MonrowAbuseError) {
// runaway loop detected for this user — fall back gracefully
} else if (err instanceof MonrowCostLimitError) {
// this user has hit their per-user spend ceiling
} else if (err instanceof MonrowPausedError) {
// your cloud panic surface paused everything — show a soft outage screen
} else if (err instanceof MonrowBlockedError) {
// this user is blocked from the panic surface — return 403 to them
} else {
throw err
}
}The monrow field is stripped from the request before it ever reaches the
upstream provider (Anthropic or OpenAI).
feature is an optional string (recommended ≤ 64 chars, hard cap 256) used
for per-feature cost attribution.
Detectors
runaway_loop (pre-call, throws)
Counts requests per user inside a sliding window. Defaults: 100 requests in
300 seconds. When the threshold is hit, the next call is blocked with a
MonrowAbuseError and a full incident card is printed to stderr.
cost_ceiling (pre-call, throws)
Per-user trailing-30-day spend. Default limit $50.00. If a user's
cumulative spend has already reached the limit, the next call throws
MonrowCostLimitError.
velocity_spike (post-call, log-only)
After each call, compares the user's last-hour request rate to their trailing
24-hour baseline. Never throws. Writes a severity: "observed" incident.
Cloud sync (Pro)
Pass an apiKey to enable cloud sync:
const monrow = new Monrow({ apiKey: process.env.MONROW_KEY })Generate keys at https://monrow.io/settings/api-keys. Each completed call
emits a CostEvent containing only metadata — event_id, user_key,
feature, model, input_tokens, output_tokens, cost_cents, timestamp.
No prompts. No responses. No PII.
Behavior:
- Events buffer in memory and on disk at
.monrow/sync-queue.jsonlfor crash recovery. - Flushes every 30 seconds or when 50 events accumulate, whichever first.
- On
401(invalid key) or402(no Pro subscription), sync disables itself for the session and logs a single warning to stderr. Local detection continues unchanged. - On
5xxor network failure, retries with exponential backoff (10s → 30s → 2m → 10m), then drops the batch after the fourth attempt. - On
SIGTERM, attempts a final flush for up to 5 seconds.
Sync failures never throw into your code.
Cloud control (Pro)
When apiKey is set, the SDK also polls
GET ${apiUrl}/v1/control/state every 15 seconds. The endpoint returns a
small JSON payload — paused_until, blocked_user_keys, version — used by
the panic surface. Polling is If-None-Match
ETag-aware so steady-state traffic is one cheap 304 Not Modified per
window.
Before every wrapped messages.create() call, the SDK checks the cached
state in memory (no network in the hot path):
- If
paused_untilis in the future → throwsMonrowPausedError. - If
monrow.useris inblocked_user_keys→ throwsMonrowBlockedError. - Otherwise the existing detectors run.
Auto-release is the safety mechanism: a pause cannot be left on by accident.
When paused_until passes, calls resume on the next poll (≤ 15 s).
The cached state is persisted to .monrow/control-state.json for crash
recovery. Polling fails open — a 5xx or network error keeps the last-known
state and retries on the next tick. A 401 disables polling for the
session and logs one warning.
Monrow Cloud does not push notifications. The panic surface is pull-based. Open https://monrow.io/control when you need to act.
CLI (local-only, free)
npx monrow doctor # transparency report — always freeRunaway loop detection, cost ceilings, and velocity spike logging work with no account and no API key.
Pro cloud ($29/mo)
Requires an API key in .monrowrc.json (from
https://monrow.io/settings/api-keys):
npx monrow report # per-user cost summary, last 30 days
npx monrow report --by-feature # per-feature cost summary, last 30 days
npx monrow incidents # list recent incidents
npx monrow incidents show <id> # re-render the full incident cardPro also includes real-time Slack/webhook alerts, AI margin intelligence dashboard, cross-server protection, and the remote kill switch at https://monrow.io.
Report and incident commands read .monrow/incidents.jsonl and
.monrow/state.json in the current directory.
Configuration
All settings are optional and have sane defaults. They can be set via the
constructor, a .monrowrc.json file in the working directory, or both
(constructor wins).
new Monrow({
// cloud sync (optional)
apiKey: process.env.MONROW_KEY, // default: null (local-only)
apiUrl: 'https://monrow.io', // default; override for self-hosted
sync_batch_size: 50, // default
sync_interval_ms: 30_000, // default
control_poll_interval_ms: 15_000, // default
// detection
detectors: {
runaway_loop: { enabled: true, threshold_count: 100, window_seconds: 300 },
cost_ceiling: { enabled: true, default_limit_cents: 5000 },
velocity_spike: { enabled: true, multiplier: 5, baseline_window_hours: 24 },
},
impact_floor_cents_per_day: 2000,
incident_log_path: '.monrow/incidents.jsonl',
silent_boot: false,
})Security
Run the transparency check at any time:
npx monrow doctorPrints exactly what files Monrow has written, what network requests it makes, what SDK hooks are active, and whether cloud sync is enabled. No network call required.
Monrow never:
- Proxies your AI requests
- Collects prompt or response content
- Installs background services or daemons
- Executes shell commands
- Writes files outside
.monrow/
Full security policy: SECURITY.md
What Monrow doesn't do
Trust matters more than features for an in-process SDK. Things Monrow explicitly does not do:
- No model swapping. Your
modelparameter is never mutated. - No prompt mutation. Your
messagespayload is never modified. - No prompt or response sync. The cloud ingest endpoint only accepts the event metadata listed above.
- No silent retries against Anthropic.
- No telemetry by default. Local-only mode makes zero outbound requests.
- No browser support. Node only. Throws on import in the browser.
- No external runtime dependencies.
Links
- GitHub: https://github.com/devmonrow/Monrow
- Site: https://monrow.io
- Settings → API Keys: https://monrow.io/settings/api-keys
- Migration from @monrow/install: https://monrow.io/docs/migrate
License
MIT.
