Timeflux is a Go-based HTTP service that implements the InfluxDB v1 API, translating requests to TimescaleDB on the backend. This allows existing systems using InfluxDB clients to seamlessly switch to TimescaleDB without code changes.
You can use this to help migrate away from InfluxDB towards TimescaleDB or keep it as a structured layer on top of TimescaleDB.
- InfluxDB v1 API Compatible: Supports write and query endpoints
- Line Protocol Support: Parse and write InfluxDB line protocol data
- InfluxQL Query Support: Translate InfluxQL queries to PostgreSQL SQL
- Influx CLI Support: Use the Influx CLI tool to interact with TimescaleDB
- Grafana Support: Use with Grafana like you would Influx
- Write-Ahead Log (WAL): 10x faster writes with crash recovery and CRC32 checksums
- Dynamic Schema Evolution: Automatically creates tables and columns as new measurements, tags, and fields appear
- Background Index Creation: Tag indexes created asynchronously to avoid blocking writes
- Concurrent Write Safety: Handles multiple concurrent writes with proper locking
- Authentication & Authorization: User management with granular database and measurement permissions
- TimescaleDB Native: Data stored in native PostgreSQL columns for easy migration
- Production-Ready: Comprehensive metrics, error handling, and graceful shutdown
| InfluxDB Concept | TimescaleDB Implementation |
|---|---|
| Database | PostgreSQL schema |
| Measurement | Hypertable (one per measurement) |
| Tags (indexed metadata) | TEXT columns with indexes |
| Fields (measured values) | Typed columns (DOUBLE PRECISION, BIGINT, TEXT, BOOLEAN) |
| Timestamp | TIMESTAMPTZ column |
When a write contains new tags or fields not seen before, the service automatically:
- Alters the table to add the new column
- Creates an index for new tag columns
- Records the change in a metadata table
- Updates its in-memory schema cache
- Go 1.21 or later
- PostgreSQL 12+ with TimescaleDB extension
- Docker (optional, for instantly running combined TimescaleDB and Timeflux)
The easiest way to get started is using Docker Compose, which will run both TimescaleDB and Timeflux:
# Clone the repository
git clone https://github.com/penguinpowernz/timeflux.git
cd timeflux
# Start both services
make up
# Check logs
make logs
# Stop services
make downThe config.yaml file is already configured to connect to the TimescaleDB container. Timeflux will be available at http://localhost:8086.
docker run -d \
--name timescaledb \
-p 5432:5432 \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=timeseries \
timescale/timescaledb:latest-pg16- Clone the repository:
git clone https://github.com/penguinpowernz/timeflux.git
cd timeflux- Create configuration file:
cp config.yaml.example config.yaml- Edit
config.yamlto match your TimescaleDB connection settings:
server:
port: 8086
database:
host: localhost # Use 'timescaledb' if running in Docker
port: 5432
database: timeseries
user: postgres
password: secret
pool_size: 32
logging:
level: info
format: json
wal:
enabled: true
path: /tmp/timeflux/wal
num_workers: 8
fsync_interval_ms: 100
segment_size_mb: 64
segment_cache_size: 2
no_sync: false # set to true for testing only (faster but no crash safety)
auth:
enabled: false # set to true to require authentication- Build the application:
make build- Run the application:
make run
# or
bin/timeflux -config config.yamlThe server will start on port 8086 (or the port specified in your config).
Timeflux supports user-based authentication and authorization with granular permissions at the database and measurement level.
Set auth.enabled: true in your config.yaml file:
auth:
enabled: trueUser management is performed via command-line interface:
Add a user:
# With auto-generated password
bin/timeflux user:add alice
# With specified password
bin/timeflux user:add alice mypasswordGrant permissions:
# Grant read/write to entire database
bin/timeflux user:grant alice mydb:rw
# Grant read-only to specific measurement
bin/timeflux user:grant alice mydb.cpu:r
# Grant write to all databases (wildcard)
bin/timeflux user:grant alice "*:w"
# Grant read to specific measurement across all databases
bin/timeflux user:grant alice "*.cpu:r"List users and permissions:
# List all users
bin/timeflux user:list
# Show user details and permissions
bin/timeflux user:show aliceOther commands:
# Reset password
bin/timeflux user:reset-password alice newpassword
# Revoke permission
bin/timeflux user:revoke alice mydb.cpu
# Delete user
bin/timeflux user:delete aliceTimeflux supports multiple authentication methods compatible with InfluxDB clients:
Query parameters:
curl 'http://localhost:8086/query?u=alice&p=password&db=mydb&q=SELECT+*+FROM+cpu'Basic Auth:
curl -u alice:password 'http://localhost:8086/query?db=mydb&q=SELECT+*+FROM+cpu'Token header (username:password):
curl -H 'Authorization: Token alice:password' \
'http://localhost:8086/query?db=mydb&q=SELECT+*+FROM+cpu'Permissions are granted at two levels:
- Database level: Access to all measurements in a database (
database:rw) - Measurement level: Access to specific measurements (
database.measurement:r)
Wildcards are supported:
*:rw- All databases, all measurements*.cpu:r- CPU measurement across all databasesmydb.*:w- All measurements in mydb (implied when usingmydb:w)
Permission precedence (most specific wins):
database.measurement(specific measurement)database.*(all measurements in database)*.measurement(specific measurement across databases)*.*(global wildcard)
Write data using InfluxDB line protocol:
curl -i -XPOST 'http://localhost:8086/write?db=mydb' \
--data-binary 'cpu_usage,host=server1,region=us-east usage_percent=85.3,load_avg=2.5 1620000000000000000'Multiple lines can be written in a single request:
curl -i -XPOST 'http://localhost:8086/write?db=mydb' \
--data-binary 'cpu_usage,host=server1 usage_percent=85.3 1620000000000000000
cpu_usage,host=server2 usage_percent=72.1 1620000000000000000
memory,host=server1 used_percent=68.5 1620000000000000000'Response: HTTP/1.1 204 No Content
Execute InfluxQL queries:
curl -G 'http://localhost:8086/query?db=mydb' \
--data-urlencode 'q=SELECT mean(usage_percent) FROM cpu_usage WHERE time > now() - 1h GROUP BY time(5m)'Response:
{
"results": [
{
"statement_id": 0,
"series": [
{
"columns": ["time", "mean"],
"values": [
["2024-02-24T10:00:00Z", 82.5],
["2024-02-24T10:05:00Z", 85.3],
["2024-02-24T10:10:00Z", 88.1]
]
}
]
}
]
}Create a database:
curl -G 'http://localhost:8086/query' \
--data-urlencode 'q=CREATE DATABASE mydb'Show all databases:
curl -G 'http://localhost:8086/query' \
--data-urlencode 'q=SHOW DATABASES'Drop a database:
curl -G 'http://localhost:8086/query' \
--data-urlencode 'q=DROP DATABASE mydb'Show all measurements:
curl -G 'http://localhost:8086/query?db=mydb' \
--data-urlencode 'q=SHOW MEASUREMENTS'Show series (unique tag combinations):
curl -G 'http://localhost:8086/query?db=mydb' \
--data-urlencode 'q=SHOW SERIES'Show tag keys for a measurement:
curl -G 'http://localhost:8086/query?db=mydb' \
--data-urlencode 'q=SHOW TAG KEYS FROM cpu_usage'Show field keys for a measurement:
curl -G 'http://localhost:8086/query?db=mydb' \
--data-urlencode 'q=SHOW FIELD KEYS FROM cpu_usage'Drop series matching specific tags:
curl -G 'http://localhost:8086/query?db=mydb' \
--data-urlencode 'q=DROP SERIES FROM cpu_usage WHERE host='\''server1'\'''Drop an entire measurement:
curl -G 'http://localhost:8086/query?db=mydb' \
--data-urlencode 'q=DROP MEASUREMENT cpu_usage'| InfluxQL | PostgreSQL / TimescaleDB Analog | Notes | Difficulty | |
|---|---|---|---|---|
| ✅ | mean(field) |
AVG(field) |
Easy | |
| ✅ | count(field) |
COUNT(field) |
Easy | |
| ✅ | sum(field) |
SUM(field) |
Easy | |
| ✅ | min(field) |
MIN(field) |
Easy | |
| ✅ | max(field) |
MAX(field) |
Easy | |
| ✅ | stddev(field) |
STDDEV(field) |
Easy | |
| ✅ | median(field) |
percentile_cont(0.5) WITHIN GROUP (ORDER BY field) |
Easy | |
| ✅ | spread(field) |
MAX(field) - MIN(field) |
Easy | |
| ✅ | mode(field) |
MODE() WITHIN GROUP (ORDER BY field) |
Easy | |
| ❌ | distinct(field) |
COUNT(DISTINCT field) |
Easy | |
| ✅ | first(field) |
FIRST(field, time) |
TimescaleDB function | Easy |
| ✅ | last(field) |
LAST(field, time) |
TimescaleDB function | Easy |
| ✅ | percentile(field, N) |
percentile_cont(N/100) WITHIN GROUP (ORDER BY field) |
N is 0–100 in InfluxQL | Easy |
| ❌ | top(field, N) |
Subquery with ORDER BY field DESC LIMIT N |
Medium | |
| ❌ | bottom(field, N) |
Subquery with ORDER BY field ASC LIMIT N |
Medium | |
| ❌ | sample(field, N) |
Subquery with ORDER BY RANDOM() LIMIT N |
Medium | |
| ✅ | now() |
NOW() |
Easy | |
| ✅ | abs(field) |
ABS(field) |
Easy | |
| ✅ | ceil(field) |
CEIL(field) |
Easy | |
| ✅ | floor(field) |
FLOOR(field) |
Easy | |
| ✅ | round(field) |
ROUND(field) |
Easy | |
| ✅ | sqrt(field) |
SQRT(field) |
Easy | |
| ✅ | pow(field, exp) |
POWER(field, exp) |
Easy | |
| ✅ | exp(field) |
EXP(field) |
Easy | |
| ✅ | ln(field) |
LN(field) |
Easy | |
| ✅ | log(field, base) |
LOG(base::numeric, field::numeric) |
Arg order swapped; both args cast to numeric | Easy |
| ✅ | log2(field) |
LOG(2::numeric, field::numeric) |
Easy | |
| ✅ | log10(field) |
LOG(10::numeric, field::numeric) |
Easy | |
| ✅ | sin(field) |
SIN(field) |
Input in radians | Easy |
| ✅ | cos(field) |
COS(field) |
Input in radians | Easy |
| ✅ | tan(field) |
TAN(field) |
Input in radians | Easy |
| ✅ | asin(field) |
ASIN(field) |
Input must be in [-1, 1] | Easy |
| ✅ | acos(field) |
ACOS(field) |
Input must be in [-1, 1] | Easy |
| ✅ | atan(field) |
ATAN(field) |
Easy | |
| ✅ | atan2(y, x) |
ATAN2(y, x) |
Same arg order | Easy |
| ❌ | cumulative_sum(field) |
Window function with SUM() OVER (ORDER BY time ROWS UNBOUNDED PRECEDING) |
Medium | |
| ❌ | derivative(field[, unit]) |
(field - LAG(field) OVER (...)) / time_diff |
Window function | Medium |
| ❌ | non_negative_derivative(field) |
GREATEST(derivative, 0) |
Medium | |
| ❌ | difference(field) |
field - LAG(field) OVER (ORDER BY time) |
Window function | Medium |
| ❌ | non_negative_difference(field) |
GREATEST(difference, 0) |
Medium | |
| ❌ | moving_average(field, N) |
AVG(field) OVER (ORDER BY time ROWS BETWEEN N-1 PRECEDING AND CURRENT ROW) |
Window function | Medium |
| ❌ | elapsed(field[, unit]) |
EXTRACT(EPOCH FROM time - LAG(time) OVER (...)) |
Window function | Medium |
| ❌ | integral(field[, unit]) |
Trapezoidal rule via window functions | Medium–Hard | |
| ❌ | histogram(field, min, max, N) |
WIDTH_BUCKET(field, min, max, N) with GROUP BY |
Medium–Hard | |
| ❌ | exponential_moving_average(field, N) |
Recursive CTE or PL/pgSQL | Hard | |
| ❌ | double_exponential_moving_average(field, N) |
Custom function | Hard | |
| ❌ | triple_exponential_moving_average(field, N) |
Custom function | Hard | |
| ❌ | triple_exponential_derivative(field, N) |
Custom function | Hard | |
| ❌ | relative_strength_index(field, N) |
Custom PL/pgSQL function | Hard | |
| ❌ | chande_momentum_oscillator(field, N) |
Custom PL/pgSQL function | Hard | |
| ❌ | kaufmans_efficiency_ratio(field, N) |
Custom PL/pgSQL function | Hard | |
| ❌ | kaufmans_adaptive_moving_average(field, N) |
Custom PL/pgSQL function | Hard | |
| ❌ | holt_winters(field, N, m) |
No direct analog — application-layer or PL/Python | Forecasting function | Very Hard |
See FUNCTIONS.md for translation strategies and implementation phases.
| Feature | Notes | |
|---|---|---|
| ✅ | SELECT with fields and aggregations |
|
| ✅ | WHERE with comparison operators (=, !=, <, >, <=, >=) |
|
| ✅ | WHERE with AND / OR and parenthesized expressions |
|
| ✅ | GROUP BY time(interval) |
Maps to time_bucket() |
| ✅ | GROUP BY tag |
|
| ✅ | ORDER BY |
|
| ✅ | LIMIT / OFFSET |
|
| ✅ | SHOW MEASUREMENTS |
|
| ✅ | SHOW TAG KEYS |
|
| ✅ | SHOW TAG VALUES WITH KEY = ... |
|
| ✅ | SHOW FIELD KEYS |
|
| ✅ | SHOW DATABASES |
|
| ✅ | SHOW SERIES |
|
| ✅ | CREATE DATABASE |
Creates a PostgreSQL schema |
| ✅ | DROP DATABASE |
Drops the PostgreSQL schema |
| ✅ | DROP SERIES FROM ... WHERE ... |
Translates to DELETE FROM |
| ✅ | DROP MEASUREMENT |
Drops the hypertable |
| ❌ | Subqueries | Not yet supported |
| ❌ | FILL() |
No equivalent translation yet |
| ❌ | INTO (write query results) |
Not yet supported |
Field types are inferred from InfluxDB line protocol:
| Line Protocol | PostgreSQL Type |
|---|---|
field=42.5 |
DOUBLE PRECISION |
field=42i |
BIGINT |
field="value" |
TEXT |
field=true or field=t |
BOOLEAN |
POST /write?db={database}- Write data in line protocol formatGET /query?db={database}&q={query}- Execute InfluxQL queryPOST /query?db={database}- Execute InfluxQL query (body contains query)GET /ping- Health check (returns 204)GET /health- Health status (returns JSON)GET /metrics- Prometheus-style metrics (JSON format)
| Status | Test | Description | Duration |
|---|---|---|---|
| ✅ | BasicWrite | Write single point with one field | 6.801163ms |
| ✅ | MultiFieldWrite | Write point with multiple fields of different types | 5.709709ms |
| ✅ | TaggedWrite | Write points with tags | 5.225282ms |
| ✅ | BatchWrite | Write batch of 100 points | 5.968928ms |
| ✅ | AllDataTypes | Write point with all supported data types | 2.576475ms |
| ✅ | SimpleSelect | SELECT * FROM measurement | 2.728004ms |
| ✅ | SelectWithWhere | SELECT with WHERE clause filtering tags | 2.804248ms |
| ✅ | SelectMean | SELECT MEAN() aggregation | 4.23132ms |
| ✅ | GroupByTime | SELECT with GROUP BY time(5m) | 7.667817ms |
| ✅ | GroupByTag | SELECT with GROUP BY tag | 6.060589ms |
| ✅ | Count | SELECT COUNT(*) | 2.649679ms |
| ✅ | Sum | SELECT SUM() | 3.559284ms |
| ✅ | MinMax | SELECT MIN() and MAX() | 4.839366ms |
| ✅ | ShowMeasurements | SHOW MEASUREMENTS | 1.762354ms |
| ✅ | ShowTagKeys | SHOW TAG KEYS | 1.027113ms |
| ✅ | ShowFieldKeys | SHOW FIELD KEYS | 1.048455ms |
| ✅ | CreateDatabase | CREATE DATABASE | 660.542µs |
| ✅ | ShowDatabases | SHOW DATABASES | 2.262632ms |
| ✅ | ShowSeries | SHOW SERIES | 1.269345ms |
| ✅ | DropSeries | DROP SERIES with WHERE clause | 112.11249ms |
| ✅ | DropMeasurement | DROP MEASUREMENT | 104.275748ms |
| ✅ | FirstLast | SELECT FIRST() and LAST() functions | 2.221247ms |
| ✅ | Percentile | SELECT PERCENTILE() function | 4.179828ms |
| ✅ | MultipleAggregations | SELECT multiple aggregations in one query | 4.536375ms |
| ✅ | ArithmeticOperations | SELECT with arithmetic operations (+, -, *, /) | 2.36909ms |
| ✅ | ComplexWhere | SELECT with complex WHERE (AND, OR, comparison operators) | 18.835663ms |
| ✅ | ShowTagValues | SHOW TAG VALUES with KEY | 1.515577ms |
| ✅ | Limit | SELECT with LIMIT clause | 1.038193ms |
| ✅ | Offset | SELECT with OFFSET clause | 1.010519ms |
| ✅ | OrderBy | SELECT with ORDER BY clause | 1.647866ms |
| ✅ | TimeRange | SELECT with time range in WHERE | 11.813937ms |
| ✅ | GroupByTimeIntervals | Test different GROUP BY time() intervals (1m, 5m, 1h) | 4.580791ms |
| ✅ | GroupByMultipleTags | SELECT with GROUP BY multiple tags | 207.422398ms |
| ✅ | NowFunction | Use NOW() function in WHERE clause | 1.275609ms |
| ✅ | BooleanFields | Query boolean fields with WHERE | 3.110882ms |
| ✅ | StringFields | Query string fields with WHERE | 1.675472ms |
| ✅ | NegativeNumbers | Query with negative numbers and zero | 205.440891ms |
| ✅ | DropSeries | DROP SERIES with WHERE clause | 106.281272ms |
| ✅ | DropMeasurement | DROP MEASUREMENT | 103.553971ms |
| ✅ | DropDatabase | DROP DATABASE | 107.279086ms |
| ✅ | WriteNumericData | Write known numeric dataset for Phase 1 function tests | 3.750134ms |
| ✅ | Stddev | SELECT STDDEV() aggregation | 1.663358ms |
| ✅ | Median | SELECT MEDIAN() aggregation | 1.048993ms |
| ✅ | Spread | SELECT SPREAD() (MAX-MIN) aggregation | 1.606488ms |
| ✅ | Abs | SELECT ABS() on negative values | 1.15031ms |
| ✅ | Ceil | SELECT CEIL() ceiling function | 1.343904ms |
| ✅ | Floor | SELECT FLOOR() floor function | 1.42966ms |
| ✅ | Round | SELECT ROUND() rounding function | 2.390172ms |
| ✅ | Sqrt | SELECT SQRT() square root function | 3.822994ms |
| ✅ | Pow | SELECT POW(field, exponent) power function | 2.146658ms |
| ✅ | Exp | SELECT EXP() exponential function | 1.17573ms |
| ✅ | Ln | SELECT LN() natural logarithm | 1.061525ms |
| ✅ | Log2 | SELECT LOG2() base-2 logarithm | 1.360194ms |
| ✅ | Log10 | SELECT LOG10() base-10 logarithm | 1.08444ms |
| ✅ | LogBase | SELECT LOG(field, base) custom base logarithm | 1.051762ms |
| ✅ | Sin | SELECT SIN() sine function (radians) | 1.112369ms |
| ✅ | Cos | SELECT COS() cosine function (radians) | 1.198539ms |
| ✅ | Tan | SELECT TAN() tangent function (radians) | 1.451375ms |
| ✅ | Asin | SELECT ASIN() arcsine (input in [-1,1]) | 1.493281ms |
| ✅ | Acos | SELECT ACOS() arccosine (input in [-1,1]) | 1.561699ms |
| ✅ | Atan | SELECT ATAN() arctangent | 1.289703ms |
| ✅ | Atan2 | SELECT ATAN2(y, x) two-argument arctangent | 1.499374ms |
Summary: 62/62 passed in 2.1136693s
/
├── main.go # Entry point, HTTP server setup
├── Makefile # Build automation
├── go.mod
├── go.sum
├── config.yaml # Configuration file
├── bin/ # Built binaries
├── config/
│ └── config.go # Configuration management
├── schema/
│ └── manager.go # Schema cache and DDL coordination
├── write/
│ ├── handler.go # Write endpoint handler
│ ├── lineprotocol.go # Line protocol parser
│ ├── wal_buffer.go # WAL buffer and worker pool
│ └── wal_entry.go # WAL entry format with CRC32 checksums
├── query/
│ ├── handler.go # Query endpoint handler
│ └── translator.go # InfluxQL to SQL translator
├── auth/
│ ├── auth.go # Credential parsing
│ ├── middleware.go # Authentication and authorization middleware
│ └── user_manager.go # User and permission management
├── usercli/
│ └── user.go # User management CLI commands
├── metrics/
│ └── metrics.go # Metrics collection
└── README.md
- InfluxDB v2 API not supported (Flux query language)
- Bearer token (JWT) authentication not yet implemented
- Single instance only (no clustering)
- Subset of InfluxQL features supported
- Measurement-level permissions extracted at database level (not from query content)
- Retention policies not implemented —
CREATE/ALTER/DROP/SHOW RETENTION POLICYare not supported. All data is written to a single default table per measurement with no automatic expiry. See RETENTION_POLICIES.md for an investigation into how this could be implemented using TimescaleDB's nativeadd_retention_policy(). - Continuous queries not implemented —
CREATE/DROP/SHOW CONTINUOUS QUERYare not supported. There is no automatic periodic aggregation or downsampling. See CONTINUOUS_QUERIES.md for an investigation into how this could be implemented using TimescaleDB continuous aggregates or pg_cron. rpwrite parameter ignored — therpquery parameter on the/writeendpoint is accepted but has no effect; all writes go to the default (unqualified) measurement table.- Fully qualified measurement names not supported — InfluxQL syntax
"retention_policy"."measurement"in queries is not parsed or translated.
- JWT/Bearer token authentication
- Tag based permissions
- Multi instance for HA setups
- Replica awareness for distributed reads with writing to the master
- Support entire InfluxDB function set
- Store metrics in an "_internal" database like InfluxDB
- Retention policy support via TimescaleDB
add_retention_policy()(see RETENTION_POLICIES.md) - Continuous query support via TimescaleDB continuous aggregates (see CONTINUOUS_QUERIES.md)
You can use Timeflux as a drop-in replacement for InfluxDB in Telegraf:
# telegraf.conf
[[outputs.influxdb]]
urls = ["http://localhost:8086"]
database = "telegraf"
skip_database_creation = trueTelegraf will write data to Timeflux, which will store it in TimescaleDB.
- Deploy Timeflux alongside your existing InfluxDB instance
- Test with one non-critical system first
- Gradually migrate other systems to point at Timeflux
- Monitor performance and adjust as needed
- Eventually retire the original InfluxDB instance
- Migrate services to native TimescaleDB queries over time
Since data is stored in regular PostgreSQL columns, migrating to native SQL queries is straightforward.
With WAL Enabled (Default):
- Write requests append to WAL and return immediately (~1-2ms)
- Background workers process WAL entries in batches
- 10x faster than synchronous writes
- Typical throughput: 500+ batches/second (vs 50 batches/second synchronous)
- Query lag: 1-5 seconds (acceptable for metrics/observability)
Synchronous Mode (WAL Disabled):
- Each write blocks until data is committed to TimescaleDB
- Uses PostgreSQL COPY for bulk inserts
- Parallel writes across different measurements
- Typical throughput: 50 batches/second
Schema Evolution:
- Tag indexes created asynchronously in background (non-blocking)
- DDL operations batched in transactions
- Per-measurement locking prevents contention
- Schema cache minimizes database round-trips
Query Performance:
- TimescaleDB automatic time-based partitioning
- Indexed tag columns for fast filtering
- Background index creation doesn't block writes
Ensure TimescaleDB is running and accessible:
docker ps | grep timescaledb
psql -h localhost -U postgres -d timeseries -c "SELECT version();"Check that the database parameter is correct:
curl 'http://localhost:8086/write?db=testdb' -d 'test value=1'Check logs for unsupported InfluxQL features. The translator currently supports a subset of InfluxQL.
MIT License
I made this tool because I love the InfluxDB v1 interface - Influx Line Protocol and InfluxQL. But I grew to dislike the backing database, being too inflexible to delete rows without crashing my InfluxCloud cluster. However PostgresQL can also be a beast despite or because of its infinite utility. Timeflux creates a nice layer providing the best of both worlds.
I hope this can be of help to others with aging InfluxDB installs who don't want to change all their infra and tooling to switch databases. I used claude to help develop this, monitoring it closely and driving it to make the correct architectural decisions while relying on it for technical advice, and options.
For developers and AI assistants working on this project, see CLAUDE.md for:
- Architecture overview
- Development guidelines
- Common issues and solutions
- Code patterns and conventions
Contributions are welcome! Please:
- Read CLAUDE.md for architecture guidance
- Open an issue to discuss major changes
- Submit pull requests with clear descriptions
- Include tests where applicable