Lapsoss captures application errors once and decides what deserves to reach Sentry, Rollbar, or another vendor before those events consume ingestion.
Rails / Puma / Sidekiq
│ scrubbed events over a local Unix socket
▼
lapsossd
fingerprint → count → filter → budget
│
▼
vendor delivery
lapsoss: pure-Ruby capture, Rails integration, context, and scrubbing. No native extension, Rust compiler, or vendor credentials in the client.lapsossd: a platform gem containing a standalone Rust executable. Shared aggregation across local workers, durable counts and event budgets, bounded delivery queues, and retries. Run it under your service supervisor.
The client requires Ruby 4.0+; Rails integration targets Rails 8.1+. The daemon currently supports Unix systems. Platform binaries must be built for each supported host; this branch does not imply they are already published.
# Gemfile
gem "lapsoss"# config/initializers/lapsoss.rb
Lapsoss.configure do |config|
config.daemon_socket = "/run/lapsoss/lapsossd.sock"
config.daemon_timeout = 0.1 # seconds, including connection and admission ACK
config.environment = Rails.env
config.release = ENV["APP_RELEASE"]
config.logger = Rails.logger
endVendor adapters and credentials belong in the daemon configuration, not
config.use_sentry in the application.
Lapsoss.capture_exception(error)
Lapsoss.capture_message("Payment processing unavailable", level: :error)
Lapsoss.with_scope(tags: { component: "checkout" }) do
Lapsoss.capture_exception(error)
end
Lapsoss.flush(timeout: 2)Rails.error integration, breadcrumbs, before-send hooks, exclusion filters, sampling, and scrubbing remain client-side. The daemon sees only events that survive those client-side policies.
Create socket and durable state directories owned by the application's service
account, with mode 0700. Socket and database files are restricted to 0600.
Keep state on persistent storage, not /run or another temporary filesystem.
{
"socket": "/run/lapsoss/lapsossd.sock",
"storage": { "path": "/var/lib/lapsoss/state.redb" },
"destinations": [
{
"adapter": {
"name": "sentry",
"type": "sentry",
"settings": { "dsn": "https://PUBLIC_KEY@example.com/PROJECT_ID" }
},
"policy": {
"timed_summaries": true,
"representative_interval_seconds": 60,
"budget_events": 100,
"budget_window_seconds": 60,
"max_fingerprints": 4096,
"retention_seconds": 3600,
"excluded_exception_classes": ["ActionController::RoutingError"]
}
}
],
"transport": { "max_retries": 0 }
}lapsossd --config /etc/lapsossd.jsonThe first admitted occurrence is queued immediately. Duplicates are counted without being forwarded until another representative is eligible. Every destination has its own policy and budget.
Admission is acknowledged only after a durable transaction stores the decision,
counts, budget, and queued payloads. Unfinished jobs replay after restart without
spending another budget token. Optional timed summaries report pending counts
even when no further occurrence arrives; they spend the same destination budget.
Without timed_summaries, counts accompany the next eligible event.
Budgets limit logical admissions—not
billing or HTTP attempts; ambiguous vendor acknowledgements can cause duplicates.
See daemon operations before deploying.
bundle install
bundle exec rake build # pure Ruby client gem
bundle exec rake daemon:build # release executable + host platform daemon gem
cargo test -p lapsossd --locked
LAPSOSSD_TEST_BIN="$PWD/target/release/lapsossd" bundle exec rake test_daemonPublishing is deliberately manual:
gem push pkg/lapsoss-2.0.0.gem
gem push pkg/lapsossd-0.1.0-<platform>.gemBuild each daemon platform gem on its target host. No installation-time compilation or executable download is used.
The independent lapsoss Rust crate stays at 0.2.0 and supplies the shared
delivery core. See its guide.
The legacy embedded Ruby guide is retained for source-checkout
compatibility; it is not the deployment path for the pure-Ruby gem.