ytti/oxidized
Oxidized is a network device configuration backup tool. It's a RANCID replacement!
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1d ago
- ✓16 active contributors
- ✓Apache-2.0 licensed
Show 3 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 55% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/ytti/oxidized)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/ytti/oxidized on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: ytti/oxidized
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/ytti/oxidized shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1d ago
- 16 active contributors
- Apache-2.0 licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 55% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live ytti/oxidized
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/ytti/oxidized.
What it runs against: a local clone of ytti/oxidized — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in ytti/oxidized | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of ytti/oxidized. If you don't
# have one yet, run these first:
#
# git clone https://github.com/ytti/oxidized.git
# cd oxidized
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of ytti/oxidized and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "ytti/oxidized(\\.git)?\\b" \\
&& ok "origin remote is ytti/oxidized" \\
|| miss "origin remote is not ytti/oxidized (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "lib/oxidized.rb" \\
&& ok "lib/oxidized.rb" \\
|| miss "missing critical file: lib/oxidized.rb"
test -f "lib/oxidized/core.rb" \\
&& ok "lib/oxidized/core.rb" \\
|| miss "missing critical file: lib/oxidized/core.rb"
test -f "lib/oxidized/cli.rb" \\
&& ok "lib/oxidized/cli.rb" \\
|| miss "missing critical file: lib/oxidized/cli.rb"
test -f "lib/oxidized/config.rb" \\
&& ok "lib/oxidized/config.rb" \\
|| miss "missing critical file: lib/oxidized/config.rb"
test -f "Gemfile" \\
&& ok "Gemfile" \\
|| miss "missing critical file: Gemfile"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/ytti/oxidized"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Oxidized is a Ruby-based network device configuration backup and version control tool that replaces RANCID. It automatically retrieves and stores configurations from 130+ network OS types (Cisco IOS, Junos, Arista EOS, Fortinet, Nokia, etc.) via SSH/Telnet, tracks changes in Git, and exposes a REST API for programmatic access and webhook integration. Monolithic Ruby application with bin/oxidized as the entry point, lib/oxidized/ containing the core engine (source inputs, output backends, models for each OS type). Device models live in lib/oxidized/model/ (e.g., lib/oxidized/model/ios.rb, lib/oxidized/model/junos.rb). Configuration is YAML-based (docs/Configuration.md). Outputs support Git, Git-Crypt, HTTP, and File backends. Sources include CSV, SQL, HTTP, and database inputs.
👥Who it's for
Network operations and infrastructure engineers who manage heterogeneous multi-vendor network environments and need automated configuration backup with Git-based version control, change tracking, and the ability to trigger backups via syslog events or API calls.
🌱Maturity & risk
Production-ready with established adoption. The project has 100+ commits, active GitHub Actions CI/CD (ruby.yml, publishdocker.yml, CodeQL), a Dockerfile for deployment, and comprehensive docs covering 130+ device models. However, the README warns that a maintainer is wanted, suggesting the lead developer has limited bandwidth.
Moderate risk from single-maintainer dependency. The codebase is large (548k Ruby LOC) with 100+ device model implementations that could diverge in quality. The explicit maintainer wanted notice suggests support may be slower. Dependency management via Gemfile and automated Dependabot updates mitigate some risk, but unplanned churn in the maintainer's availability could impact critical infrastructure tooling.
Active areas of work
Active maintenance with Dependabot updates enabled, CodeQL security scanning, Docker image publishing, and Ruby version testing in CI. Recent work appears focused on dependency updates and security scanning rather than major feature development. The TODO.md file likely contains prioritized next steps for contributors.
🚀Get running
git clone https://github.com/ytti/oxidized.git
cd oxidized
bundle install
bin/oxidized --help
For Docker: docker build -t oxidized . && docker run -it oxidized
Daily commands:
bundle exec oxidized
For console REPL: bundle exec bin/console. Configuration file location: ~/.config/oxidized/config by default. Outputs configurations to ~/.config/oxidized/configs/ and Git repos.
🗺️Map of the codebase
lib/oxidized.rb— Main entry point and gem loader that initializes all core components and their dependencies.lib/oxidized/core.rb— Core orchestration engine that manages device polling threads, scheduling, and the main event loop.lib/oxidized/cli.rb— Command-line interface and CLI argument parsing; defines how the application is invoked.lib/oxidized/config.rb— Configuration parser and manager; all settings flow through here and affect runtime behavior.Gemfile— Defines all Ruby dependencies; critical for understanding transitive dependencies and version constraints.lib/oxidized/hook.rb— Hook framework abstraction that enables post-backup notification and webhook plugins.
🧩Components & responsibilities
- Core Engine (lib/oxidized/core.rb) (Ruby Thread, Queue, Mutex) — Maintains device queue, schedules polling threads, manages threading pool, coordinates output/hook execution, handles config reload via API.
- Failure mode: If core
🛠️How to make changes
Add Support for a New Network Device OS
- Create a new model class in lib/oxidized/model/ that inherits from Oxidized::Model (
lib/oxidized/model/[device_os].rb) - Define prompts, commands, and parsing logic specific to the device OS using cmd() and expect() DSL (
lib/oxidized/model/[device_os].rb) - Map the model to the source inventory by setting hostname groups or model selectors (
docs/Model-Notes/[DeviceOS].md) - Test the model using the unit test framework in lib/oxidized/model/cmd.rb (
docs/ModelUnitTests.md)
Add a New Output Backend (e.g., S3, Splunk)
- Create output handler in lib/oxidized/output/[backend_name].rb inheriting from Oxidized::Output (
lib/oxidized/output/[backend_name].rb) - Implement save(node, output) method to persist device configurations to your storage backend (
lib/oxidized/output/[backend_name].rb) - Add configuration section to docs/Outputs.md documenting the output options and credentials (
docs/Outputs.md)
Add a New Hook for Post-Backup Notifications
- Create hook class in lib/oxidized/hook/[service_name].rb inheriting from Oxidized::Hook (
lib/oxidized/hook/[service_name].rb) - Implement notify_* methods (notify_success, notify_failure) to send alerts on config changes (
lib/oxidized/hook/[service_name].rb) - Configure the hook in ~/.config/oxidized/config with service credentials and channels (
docs/Hooks.md)
Add a New Device Inventory Source
- Create source handler in lib/oxidized/source/[source_type].rb inheriting from Oxidized::Source (
lib/oxidized/source/[source_type].rb) - Implement load() method to fetch device list from your inventory system (CMDB, API, database) (
lib/oxidized/source/[source_type].rb) - Add configuration examples to docs/Sources.md showing authentication and discovery options (
docs/Sources.md)
🔧Why these technologies
- Ruby with Threads — Lightweight concurrency for managing hundreds of parallel device connections with automatic thread pool scaling based on retrieval interval.
- Git as Primary Output Backend — Provides built-in version control, diff tracking, blame attribution, and distributed backup capability without additional infrastructure.
- REST API — Enables integration with monitoring systems, manual backup triggers, and external orchestration tools without requiring direct CLI access.
- Pluggable Model System — Supports 130+ OS types via lightweight DSL-based models rather than monolithic device drivers, reducing maintenance burden.
⚖️Trade-offs already made
-
Single-threaded Ruby process with manual thread pool management instead of multi-process architecture
- Why: Simplifies state management, shared device queue, and configuration reloading; avoids IPC overhead.
- Consequence: Limited to vertical scaling; large deployments (1000+ devices) may require sharding or multiple Oxidized instances.
-
Git as primary state store instead of dedicated database
- Why: Zero additional dependencies, built-in audit trail, and works offline; leverages existing git infrastructure.
- Consequence: Large config repositories (50k+ files) can cause git command slowdown; requires periodic garbage collection.
-
Synchronous blocking I/O for device connectivity
- Why: Simpler model DSL and easier device interaction logic compared to async frameworks.
- Consequence: Threads block on device latency; high-latency networks reduce concurrency efficiency.
🚫Non-goals (don't propose these)
- Does not provide real-time configuration push/remediation—only backward-looking backup and archival.
- Does not handle device authentication directly—relies on SSH keys, .netrc, or credentials passed via config.
- Does not serve as a primary config management system—intended as a RANCID replacement for auditing, not orchestration.
- Does not provide a web UI for configuration browsing—REST API only; git or external tools handle the interface.
- Does not support Windows devices running natively—SSH/Telnet are Unix-centric; Windows hosts require Linux environment.
🪤Traps & gotchas
- Configuration path: Oxidized expects ~/.config/oxidized/config by default; specify
--configflag or set environment variables for non-standard locations. 2. Threading model: The tool spawns threads per device based on configured retrieval interval; resource constraints on the host limit concurrent connections. 3. SSH keys and auth: Each OS model requires appropriate credentials (SSH keys, passwords); some models need privileged mode enabled on devices. 4. Model-specific quirks: Each OS type has device-specific interactions documented in docs/Model-Notes/ (e.g., IOS.md, JunOS.md); some devices require specific command sequences or timeouts. 5. Git output requirements: The Git output backend expects git to be installed and configured; it uses git blame to track config changes per user. 6. Hook execution context: Syslog hooks and HTTP webhooks run in the Oxidized process context; blocking operations can stall the backup loop.
🏗️Architecture
💡Concepts to learn
- Thread pool with dynamic sizing — Oxidized automatically scales worker threads to maintain a target retrieval interval across many devices; understanding this prevents over-provisioning or under-capacity deployments
- Event-driven syslog hook triggering — Oxidized can listen for syslog events (e.g., IOS config change notifications) and immediately queue backups; this is core to reducing backup latency
- SSH/Telnet expect-style command sequencing — Device models implement command sequences with pattern matching (send command, wait for prompt, parse output); failures in expect logic cause backup failures
- Git blame for change attribution — The Git output backend stores user identity (extracted from syslog hooks) in commits so git blame shows which network operator changed each config line
- Model inheritance and polymorphism — Device models inherit from a base Model class and override methods for command sequences; understanding this pattern is essential for contributing new OS types
- YAML configuration with ERB templating — Oxidized config files support embedded Ruby (ERB); operators can use environment variables and conditionals without additional tooling
- REST API idempotency for node operations — The API supports GET/PUT /node/next/[NODE] to requeue backups; understanding idempotency prevents duplicate fetches when webhooks retry
🔗Related repos
ytti/rancid— RANCID is the predecessor tool that Oxidized replaces; understanding RANCID's design clarifies Oxidized's improvementsnetbox-community/netbox— NetBox is a complementary network inventory and IPAM system; many Oxidized deployments use NetBox as the device source via HTTP inputnetworktocode/nornir— Nornir is a modern Python alternative for network automation; users comparing Oxidized should understand this ecosystemnapalm-automation/napalm— NAPALM provides network device APIs; some Oxidized models could potentially use NAPALM drivers for abstraction (alternative to device-specific models)telegraf-plugins/telegraf— Telegraf is often paired with Oxidized in monitoring stacks to collect device metrics while Oxidized handles config backup
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for model creation and validation
The docs/Creating-Models.md exists but there are no visible unit tests in the repo structure for model validation. Given that Oxidized supports 130+ OS types with device models, adding a test suite (likely in spec/ or test/) for model loading, parsing, and output validation would catch regressions early. This is critical since each model is a customer-facing feature.
- [ ] Create spec/lib/oxidized/model_spec.rb (or equivalent) to test model instantiation
- [ ] Add tests for common model patterns (cmd execution, output parsing, regex patterns)
- [ ] Reference existing models in docs/Model-Notes/ as test fixtures
- [ ] Ensure tests validate both successful and failure cases for device simulation (see docs/DeviceSimulation.md)
Add input source unit tests for all input types
docs/Inputs.md describes multiple input sources but there's no clear test coverage visible in the repo structure. Input sources (SSH, Telnet, SNMP, etc.) are critical paths that directly affect reliability. Adding comprehensive unit tests for src/oxidized/inputs/ would significantly improve code quality and catch edge cases in credential handling, connection timeouts, and authentication failures.
- [ ] Create spec/lib/oxidized/input/ directory with tests for each input type
- [ ] Add mocking for SSH/Telnet/SNMP connections to avoid external dependencies
- [ ] Test credential validation, timeout handling, and error recovery paths
- [ ] Reference docs/Inputs.md configuration examples in test setup
Add output module integration tests and document output-specific error handling
docs/Outputs.md describes multiple output backends (Git, HTTP, Syslog, etc.) but lacks visible test coverage for output plugins. Given that outputs handle persistence and notifications, adding integration tests (especially for Git output which is likely most-used) and documenting failure scenarios and rollback behavior would improve reliability and help new contributors understand the output interface.
- [ ] Create spec/lib/oxidized/output/ with tests for Git, HTTP, and other major output types
- [ ] Add tests for edge cases: Git merge conflicts, failed HTTP requests, permission errors
- [ ] Document error handling patterns in docs/Outputs.md with examples
- [ ] Verify output tests work in CI by updating .github/workflows/ruby.yml to run them
🌿Good first issues
- Add missing test coverage for model layer: Many device models in lib/oxidized/model/ lack unit tests; pick an underused model (check git logs for last modification) and write spec/oxidized/model/modelname_spec.rb. 2. Expand Model-Notes documentation: Several supported OS types in docs/Model-Notes/ are minimal (e.g., GrandstreamHT8xx.md, LinuxGeneric.md); test against a simulator or device and document connection requirements, command output formatting, and known issues. 3. Create example hooks for common workflows: docs/Hooks.md exists but lacks concrete runnable examples; add a hooks/ directory with sample scripts for PagerDuty notifications, Slack alerts on config changes, and Jira ticket creation triggered by device backups.
⭐Top contributors
Click to expand
Top contributors
- @robertcheramy — 55 commits
- @dependabot[bot] — 7 commits
- @mklopocki — 6 commits
- @ytti — 6 commits
- @iriseden — 6 commits
📝Recent commits
Click to expand
Recent commits
fc201bb— Merge pull request #3809 from ytti/feat/aoscx-update (robertcheramy)35219db— aoscx: Hide total power consumption and simplify show system (robertcheramy)470c44a— Merge pull request #3808 from ytti/feat/3798-ios-motherboard (robertcheramy)4cb9e09— ios: keep motherboard information in show version (robertcheramy)bbddc40— Merge pull request #3802 from ytti/feat/support-option (robertcheramy)e234286— Merge remote-tracking branch 'ytti/master' into feat/support-option (robertcheramy)aa50773— Merge pull request #3719 from mklopocki/grandstreamht8xx (robertcheramy)50a8f6c— Update Supported-OS-Types.md (mklopocki)d8e1c18— Update Supported-OS-Types.md (mklopocki)e43bb29— Update CHANGELOG.md (mklopocki)
🔒Security observations
Failed to generate security analysis.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.