lineofflight/frankfurter
67.0
Adequate · 19 September 2026
12.6k
lines of production code
Ruby
primary language
1
measurement over time
What this system is
Frankfurter is a foreign exchange rate API service that aggregates and normalizes currency data from numerous central bank providers. It maintains a robust backend using SQLite and Roda, featuring a sophisticated blending engine that computes consensus rates and materializes daily, weekly, and monthly rollups for high-performance querying. The system exposes these capabilities through a versioned API (v1 and v2) with support for complex date ranges, multiple output formats, and detailed provider metadata, while ensuring data integrity through automated health monitoring and rigorous testing.
How it got here
2012 — Frankfurter rebrand and architecture migration
6 changes.
The project was rebranded to Frankfurter and underwent a major architectural shift, migrating from Sinatra and PostgreSQL to Roda and SQLite. This period introduced a new Puma web server, replaced legacy dependencies with modern libraries, and implemented materialized blended rate tables to optimize API performance.
2014–2026 — Infrastructure overhaul and API v2 launch
15 changes.
This period focused on a comprehensive backend restructuring, including a database schema redesign for performant rate storage, the introduction of a provider adapter framework, and the launch of a new v2 API with streaming support. Operational stability was enhanced through new CLI tools, automated health monitoring, and TLS certificate fixes, while extensive test coverage and code quality tools ensured the reliability of these significant architectural changes.
Features
Introduce materialized blended rate tables and parity verification
The library now materializes daily, weekly, and monthly blended rates into dedicated database tables to serve API responses from pre-computed data rather than recalculating on every request. This includes new models for daily (\BlendedRate\), weekly (\BlendedWeeklyRate\), and monthly (\BlendedMonthlyRate\) blends, along with a \Blender\ class that handles cross-provider consensus, outlier filtering, and recency-weighted averaging. A \BlendParity\ test harness is added to verify that the materialized table outputs match the live computation path byte-for-byte, ensuring data consistency during the migration.
lib · high confidence
New CLI tools for interactive console, provider health monitoring, and job scheduling
The application now includes three new executable scripts in the bin directory to improve operational visibility and automation. \bin/console\ provides an interactive Ruby shell with the application environment pre-loaded for debugging and exploration. \bin/provider\_health.rb\ acts as an automated monitor that checks provider status against the API, creating or updating GitHub issues to flag stale data or unknown currency codes, and closing them when health is restored. \bin/schedule\ replaces the previous daemon approach by using Rufus::Scheduler to manage background tasks, including staggered provider backfills on startup, periodic re-blending of rates at midnight, and executing provider-specific cron jobs based on their individual schedules.
bin · high confidence
New RuboCop cop to enforce single-line comment wrapping
A new custom RuboCop cop, \CommentFill\, has been added to automatically detect and fix comment lines that wrap early when the first word of the following line could fit on the current line. This tool helps maintain cleaner, more compact code comments by merging split comment lines where appropriate, reducing visual clutter without changing the logical structure of the code.
rubocop · high confidence
New provider adapter framework and initial bank integrations
The provider layer has been refactored to use a dedicated adapter pattern, introducing a base \Provider::Adapters::Adapter\ class that standardizes HTTP fetching via \http.rb\, response validation, and rate parsing. This change adds support for numerous central banks and monetary authorities, including AMCM, BAM, Banguat, BANREP, BANXICO, BBK (Deutsche Bundesbank historical), BCB, BCBO, BCC, BCCCD, BCCH, BCCR, BCEAO, BCP, BCRA, BCT, and BCU, enabling the system to fetch exchange rates from these new sources.
lib/provider · high confidence
Removals
Removal of legacy public landing page
The static landing page at public/index.html has been removed. This file previously served as the main entry point for the application, displaying a header describing the service as a free JSON API for foreign exchange rates and including embedded Google Analytics tracking code. Users will no longer see this specific HTML page when accessing the public directory.
public · high confidence
Behavioural changes
Add missing TLS intermediates to fix certificate verification failures
The application now includes specific intermediate CA certificates (DigiCert Global G2, RapidSSL, Sectigo, and Trustico) in its bundled trust store. This change resolves \certificate verify failed\ errors that occurred when connecting to certain servers (such as bank-of-algeria.dz, cbo.gov.om, brb.bi, bcv.org.ve, cbs.sc, and rbv.gov.vu) which serve leaf certificates without the necessary intermediate chain. By providing these missing links, the application can successfully verify TLS connections to these sites without relying on the client's system root store or network-based fetches.
_config/ca\bundles · high confidence
Database schema overhaul and historical data corrections
The database schema has been significantly restructured to support more accurate and performant rate storage. The original 'currencies' table was replaced by a 'rates' table that explicitly tracks base and quote currencies, while a new 'providers' table manages source metadata, including a shift from a generic URL to specific data and terms URLs, and a move from separate publish time/day fields to a unified publish schedule. To improve query performance, multiple indexes were added to the rates, weekly\_rates, and monthly\_rates tables. The system now materializes weekly and monthly rollups and introduces a 'blended\_rates' table for aggregated cross-currency values. Crucially, several migrations correct historical data integrity: provider keys were standardized (e.g., BOJ to BOJA, BOT to BOTA, BNM to NBM, MONGOLBANK to BOM), and specific currency codes were relabeled to reflect redenominations or standardizations (AZN to AZM for Lithuania, SDR to XDR for Tanzania and Australia, and various predecessor codes for Turkmenistan, Zambia, Russia, Brazil, and Tajikistan). Additionally, float noise was stripped from stored rates, and bid/ask components were retained to ensure accurate midpoint calculations.
db/migrate · high confidence
Deprecate v1 API and introduce v2 with new endpoints and streaming support
The v1 API is now marked as deprecated and returns standard deprecation headers (RFC 9745) pointing users to the v2 successor endpoints. The new v2 API introduces a redesigned structure with dedicated routes for individual currency pairs (\/rate\), provider-specific queries (\/providers\), and currency details (\/currency\), while replacing the old date-path syntax with query parameters for date ranges. v2 also adds support for CSV and NDJSON output formats for rate queries, implements streaming for large date-range exports to improve performance, and enforces stricter validation and error handling semantics.
lib/versions · high confidence
Migrate web server from Unicorn to Puma
The application's web server configuration has switched from Unicorn to Puma. The previous Unicorn configuration file has been removed and replaced with a new Puma configuration that allows tuning the number of worker processes and threads via environment variables, enables application preloading, and ensures database connections are properly disconnected before forking workers.
config · high confidence
New rake tasks for data maintenance, testing, and code quality
The application now includes a comprehensive set of Rake tasks in lib/tasks to manage data lifecycle and development workflows. Users can rebuild materialized blend tables (daily, weekly, monthly) and verify data parity, purge invalid future-dated rates, and backfill provider data with parallelized workers. The test suite has been refactored to run in parallel using SQLite, and a default task now automatically runs RuboCop linting, database migrations, seeding, and specs.
lib/tasks · high confidence
Rebrand to Frankfurter and migrate to SQLite, Puma, and Roda
The application is rebranded from Fixer.io to Frankfurter and undergoes a significant architectural shift: the database backend moves from PostgreSQL to SQLite, the web server replaces Unicorn with Puma, and the framework switches from Sinatra to Roda. These changes are accompanied by a new multi-stage Dockerfile, updated deployment instructions, and a streamlined boot process.
(repo-wide) · high confidence
Refactor v1 query handling and introduce currency name lookups
The v1 API layer has been restructured to improve query parsing and data availability. A new \Query\ class now centralizes the parsing of request parameters (such as \from\, \to\, \amount\, and date ranges) into a standardized hash, replacing previous inline logic. Additionally, a new \CurrencyNames\ component has been added to the v1 namespace, which fetches available currency codes from recent ECB rates and maps them to their human-readable names using the \Money\ gem, making this metadata accessible to API consumers.
lib/versions/v1 · high confidence
Skills directory converted to a symbolic link
The .claude/skills path is now a symbolic link pointing to ../.agents/skills, consolidating skill definitions under the .agents directory structure.
.claude · high confidence
V2 rate queries now use materialized blends with heavy-load protection
The V2 API implementation has shifted from live computation to serving pre-materialized weekly and monthly blends for range queries, significantly improving performance and consistency. To prevent system overload during heavy range computations, the system now enforces concurrency caps and request timeouts, returning a retry-after error if limits are exceeded. Additionally, the response format now includes provider metadata when requested via the \expand=providers\ parameter and ensures that single-provider rates are correctly anchored and sorted.
lib/versions/v2 · high confidence
Test coverage
Added comprehensive test coverage for core rate blending and API behavior; Added test coverage for V1 versioning components; Added test coverage for V2 rate blending, provider handling, and query logic; Added test coverage for provider adapters and base adapter logic; Added test coverage for v1 and v2 API endpoints; Added tests for the new CommentFill cop.
Dependencies
Major dependency overhaul and framework migration
The application has undergone a significant dependency update, migrating the web framework from Sinatra to Roda and replacing the Unicorn web server with Puma. The database driver has switched from PostgreSQL (pg) to SQLite3, and JSON generation now utilizes the Oj and Ox libraries instead of yajl-ruby. Additional changes include upgrading Rack to version 3.2.7, adding the http gem for HTTP requests, and introducing a suite of Rubocop linters (minitest, performance, rake, sequel) for development and testing.
(dependencies) · high confidence
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
How this codebase got here
Baseline
- First survey — no prior run to compare against. CAI 67.
Lenses
- Code Health 97
- Architecture 66
- Maturity 60
- Readiness 70
- Security 80
- Domain Modelling 100
Changes since last survey
- 300 commits — 272 feature/other, 28 fixes
By area
- (root) — 161 commits
- lib/provider — 34 commits
- db/seeds — 29 commits
- .github/workflows — 16 commits
- lib/versions — 10 commits
- spec/vcr_cassettes — 8 commits
- spec/versions — 6 commits
- .agents/skills — 5 commits
- db/migrate — 4 commits
- lib/tasks — 3 commits
- .claude/skills — 2 commits
- docs/superpowers — 2 commits
- lib/app.rb — 2 commits
- lib/public — 2 commits
- .github/dependabot.yml — 1 commit
- bin/provider_health.rb — 1 commit
- bin/schedule — 1 commit
- bin/wait-for-it.sh — 1 commit
- config/brb_ca_bundle.pem — 1 commit
- lib/blended_rate.rb — 1 commit
Notable commits
- fix: Fetch HMRC rates on publication, fix SLE 2022 (#665)
- fix: Fix BANREP data_url to datos.gov.co
- fix: Fix BCBO daily parse for 2026-07 layout (#549)
- fix: Fix BNR XAU unit and current-year archive fetch (#326)
- fix: Fix BOTA fetch to pass antiforgery token (#499)
- fix: Fix BRB TLS chain with bundled intermediate (#682)
- fix: Fix Banco Central de Bolivia (BCBO) data_url (#506)
- fix: Fix CBC false missed-publishes from monthly batching (#498)
- fix: Fix ECB adapter crash on nil OBS_VALUE
- fix: Fix IMF parser to read Continued blocks (#318) (#320)
- fix: Fix SBP provider workbook URLs (#565)
- fix: Fix V2 latest /rates response sort order with carry-forward (#494)
- fix: Fix Wise API skill: target only accepts a single currency
- fix: Fix duplicate first row in v2 range queries (#356)
- fix: Fix euro/ECU boundary in legacy-currency rates (#523)
- fix: Fix flaky native-precision witness row (#551)
- fix: Fix provider publish schedules and NBG weekend skip
- fix: Fix rollup purge dropping current period buckets (#521)
- fix: Fix rubocop offenses
- fix: Fix scheduler startup stagger
- …and 280 more
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
Survey your own repository
lineofflight/frankfurter was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.
About this page
- The score is its most recent published measurement, taken on 19 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
- Measured at commit f12080726862d80b80a9e77250ce58cd0fc08424 — the exact code this score is about.
- Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
- Measured by watchdog.canine.dev using codehealth-analyzer preprod-13a154b7f5d1.