Cloudsteading
Product image still needed. This listing has source documentation, but no reviewed screenshot yet.

nodrix

Self-managed IoT telemetry, realtime dashboards and device control on Cloudflare.

nodrix is a self-hosted Blynk/Ubidots alternative built on Cloudflare (D1, Durable Objects, KV, R2, Workers). Free tier eligible within limits. Inspect the source and license in the linked repository.

Source & license

Upstream license: MIT

License TL;DR

You can use it, change it, self-host it and sell it. Keep the original copyright and license notice with copies of the code. You don’t have to publish your changes. The authors don’t promise it will work.

Explain MIT in plain English →

Summary of the main license. Separate packages and assets can have different terms.

Inspect repository ↗Read this project’s actual license ↗

Repository owner

@decoded-cipher

See the upstream repository for the original creator and contributors.

Maintain this project? Maintainer verification →

Cloudflare hosting

Free tier eligible within limits

The reviewed Cloudflare deployment is eligible for Free-plan allowances for the stated small workload and feature scope. Usage limits, CPU, required account setup and separate services apply.

Hosting requirements
  • Provide your own physical hardware and network connection; Cloudflare runs the telemetry/control platform.
  • Use the pinned repository build with explicitly provisioned D1/KV/R2 IDs; the one-click carrier otherwise pulls a moving upstream revision.
  • Keep Worker requests/10 ms CPU, D1/KV/R2 and SQLite DO requests/duration/storage within free quotas; low telemetry frequency and bounded history are essential.
  • Provisioning Workflow shares Worker request/CPU quotas and includes 3,000 steps/day plus 1 GB-month state on Free; retained instance state counts.
  • Optional email, chat and OAuth integrations have separate provider accounts/costs; core owner password setup does not require them.
  • Workers Free dynamic requests are shared across this account (100,000/day), with 10 ms CPU per invocation; workload fit is conditional and has not been measured.
  • D1 Free allowance: 5 million rows read/day, 100,000 rows written/day and 5 GB total storage; unindexed scans and history retention consume quota.
  • KV Free allowance: 100,000 keys read/day and 1,000 each writes/deletes/list requests/day; this may constrain updates before Worker request limits.
  • R2 Standard allowance: 10 GB-month storage, 1 million Class A and 10 million Class B operations/month; account activation may require billing setup, and other storage classes are excluded.
  • Only SQLite Durable Objects qualify for Workers Free. Keep DO requests below 100,000/day, active duration below 13,000 GB-s/day and SQLite storage/operations inside the captured allowances.
Check current pricing ↗
Sources checked 01/10/2026

Repository snapshot: 8fd634b. Hosting eligibility reflects the deployment documentation and listed assumptions.

  • blynk ↗

    every widget is a framework-agnostic Web Component you can lift straight into your own app. - 🎮 **Two-way control** — toggles, sliders, color pickers, and buttons write values back to hardware via short polls or a control socket. - 🤖 **Visual automations** — variable, schedule, sunrise/sunset, and event triggers run conditions and actions: webhooks, code snippets, and service integrations. - 🔌 **Integrations** — fan out to HTTP, email, and chat (Slack, Telegram, Discord, and more). - 📖 **Clean read API** — latest state, time-series, and variable listings behi

  • ubidots ↗

    olor pickers, and buttons write values back to hardware via short polls or a control socket. - 🤖 **Visual automations** — variable, schedule, sunrise/sunset, and event triggers run conditions and actions: webhooks, code snippets, and service integrations. - 🔌 **Integrations** — fan out to HTTP, email, and chat (Slack, Telegram, Discord, and more). - 📖 **Clean read API** — latest state, time-series, and variable listings behind one token. - 🧠 **Native MCP server** — read telemetry and control hardware from an AI assistant, on devices you are not physically next

  • workers ↗

    name = "nodrix" main = "worker/src/index.ts" compatibility_date = "2025-05-01" compatibility_flags = ["nodejs_compat"] # This is the LOCAL-DEV config. Production deploys use deploy/wrangler.toml — # the Deploy to Cloudflare button clones only the deploy/ subdir as the user's # repo root (see deploy/wrangler.toml). The two must be kept in sync when # bindings change; they can't be symlinked (the subdir must be self-contained). # # Build pipeline. scripts/build-from-upstream.sh, whe

  • d1 ↗

    riables, dashboards, tokens). # NEVER any telemetry point. See plan §3. [[d1_databases]] binding = "DB" database_name = "nodrix" database_id = "PLACEHOLDER_FILLED_BY_DEPLOY_OR_WRANGLER" migrations_dir = "worker/src/platform/db/migrations" # R2 — cold telemetry history, NDJSON partitioned by project + hour. [[r2_buckets]] binding = "R2" bucket_name = "nodrix-telemetry" # KV — read cache and OAuth provider storage (aliased to OAUTH_KV in worker entry). [[kv_namespaces]] binding = "KV" id = "PLACEHOLDER_FILLED_BY_DEPLOY_OR_WRANGLER" # Durable Objects. [[durable_ob

  • kv ↗

    ON partitioned by project + hour. [[r2_buckets]] binding = "R2" bucket_name = "nodrix-telemetry" # KV — read cache and OAuth provider storage (aliased to OAUTH_KV in worker entry). [[kv_namespaces]] binding = "KV" id = "PLACEHOLDER_FILLED_BY_DEPLOY_OR_WRANGLER" # Durable Objects. [[durable_objects.bindings]] name = "PROJECT_DO" class_name = "ProjectDO" [[durable_objects.bindings]] name = "DASHBOARD_DO" class_name = "DashboardDO" # Singleton scheduler: one alarm at the next schedule/sunset fire time. [[durable_objects.bindings]] name = "SCHEDULER_DO" class_name

  • r2 ↗

    "PLACEHOLDER_FILLED_BY_DEPLOY_OR_WRANGLER" migrations_dir = "worker/src/platform/db/migrations" # R2 — cold telemetry history, NDJSON partitioned by project + hour. [[r2_buckets]] binding = "R2" bucket_name = "nodrix-telemetry" # KV — read cache and OAuth provider storage (aliased to OAUTH_KV in worker entry). [[kv_namespaces]] binding = "KV" id = "PLACEHOLDER_FILLED_BY_DEPLOY_OR_WRANGLER" # Durable Objects. [[durable_objects.bindings]] name = "PROJECT_DO" class_name = "ProjectDO" [[durable_objects.bindings]] name = "DASHBOARD_DO" class_name = "DashboardDO"

  • durable-objects ↗

    "PLACEHOLDER_FILLED_BY_DEPLOY_OR_WRANGLER" # Durable Objects. [[durable_objects.bindings]] name = "PROJECT_DO" class_name = "ProjectDO" [[durable_objects.bindings]] name = "DASHBOARD_DO" class_name = "DashboardDO" # Singleton scheduler: one alarm at the next schedule/sunset fire time. [[durable_objects.bindings]] name = "SCHEDULER_DO" class_name = "SchedulerDO" # MCP server agent (Streamable HTTP). The `agents` library keys one DO instance # per client session, so requests never funnel through a singleton. Only spun up # when the owner-gated /v1/mcp endpoint i

  • workflows ↗

    riven by the SchedulerDO alarm, not a Workflow.) [[workflows]] name = "nodrix-provision" binding = "PROVISION" class_name = "Provision" # Plaintext vars. Settings → Version & updates polls the upstream repo to tell # owners when a newer version is available (latest release, or default-branch # HEAD on the edge channel). Fork the upstream and override this if you're # tracking a different canonical source. [vars] NODRIX_UPSTREAM_REPO = "decoded-cipher/nodrix" # No secrets are required at deploy time. The signing secret used for # session cookies and OAuth-secret

  • free-tier-eligible ↗

    name = "nodrix" main = "worker/src/index.ts" compatibility_date = "2025-05-01" compatibility_flags = ["nodejs_compat"] # This is the LOCAL-DEV config. Production deploys use deploy/wrangler.toml — # the Deploy to Cloudflare button clones only the deploy/ subdir as the user's # repo root (see deploy/wrangler.toml). The two must be kept in sync when # bindings change; they can't be symlinked (the subdir must be self-contained). # # Build pipeline. scripts/build-from-

  • free-tier-eligible ↗

    ount Manager. | | Requests<sup>1, 2, 3, 4</sup> | Duration | CPU time | | --- | --- | --- | --- | | **Free** | 100,000 per day | No charge for duration | 10 milliseconds of CPU time per invocation | | **Standard** | 10 million included per month <br> +$0.30 per additional million | No charge or limit for duration | 30 million CPU milliseconds included per month<br> +$0.02 per additional million CPU milliseconds<br><br> Max of [5 minutes of CPU time](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) per invocation (default: 30 second

  • free-tier-eligible ↗

    rs Paid](https://developers.cloudflare.com/workers/platform/pricing/#workers) | | --- | --- | --- | | Rows read | 5 million / day | First 25 billion / month included + $0.001 / million rows | | Rows written | 100,000 / day | First 50 million / month included + $1.00 / million rows | | Storage (per GB stored) | 5 GB (total) | First 5 GB included + $0.75 / GB-mo | Track your D1 usage To accurately track your usage, use the [meta object](https://developers.cloudflare.com/d1/worker-api/return-object/), [GraphQL Analytics API](https://developers.cloudflare.com/d1/obs

  • free-tier-eligible ↗

    flare.com/workers/platform/pricing/). | | Free plan<sup>1</sup> | Paid plan | | --- | --- | --- | | Keys read | 100,000 / day | 10 million/month, + $0.50/million | | Keys written | 1,000 / day | 1 million/month, + $5.00/million | | Keys deleted | 1,000 / day | 1 million/month, + $5.00/million | | List requests | 1,000 / day | 1 million/month, + $5.00/million | | Stored data | 1 GB | 1 GB, + $0.50/ GB-month | <sup>1</sup> The Workers Free plan includes limited Workers KV usage. All limits reset daily at 00:00 UTC. If you exceed any one of these limits, further o

  • free-tier-eligible ↗

    f you have retrieved data (for infrequent access storage) for 1.1 GB, you will be billed for 2 GB. ### Free tier You can use the following amount of storage and operations each month for free. | | Free | | --- | --- | | Storage | 10 GB-month / month | | Class A Operations | 1 million requests / month | | Class B Operations | 10 million requests / month | | Egress (data transfer to Internet) | Free <sup>[1](#user-content-fn-1)</sup> | Caution The free tier only applies to Standard storage, and does not apply to Infrequent Access storage. ### Storage usage S

  • free-tier-eligible ↗

    ute and storage. Note Durable Objects are available both on Workers Free and Workers Paid plans. - **Workers Free plan**: Only Durable Objects with [SQLite storage backend](https://developers.cloudflare.com/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class) are available. - **Workers Paid plan**: Durable Objects with the SQLite storage backend are available. The [key-value storage backend](https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/#storage-backends) is only avail

  • free-tier-eligible ↗

    CPU time. Note Step count does not include rollback handlers or retries. ### Workflows pricing | Unit | Workers Free | Workers Paid | | --- | --- | --- | | Requests (millions) | 100,000 per day ([shared with Workers requests](https://developers.cloudflare.com/workers/platform/pricing/#workers)) | 10 million included per month + $0.30 per additional million | | CPU time (ms) | 10 milliseconds of CPU time per invocation | 30 million CPU milliseconds included per month + $0.02 per additional million CPU milliseconds | | Storage (GB-mo) | 1 GB-month | 1 GB-month

  • MIT ↗

    MIT License Copyright (c) 2026 Arjun Krishna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this

  • architecture ↗

    name = "nodrix" main = "worker/src/index.ts" compatibility_date = "2025-05-01" compatibility_flags = ["nodejs_compat"] # This is the LOCAL-DEV config. Production deploys use deploy/wrangler.toml — # the Deploy to Cloudflare button clones only the deploy/ subdir as the user's # repo root (see deploy/wrangler.toml). The two must be kept in sync when # bindings change; they can't be symlinked (the subdir must be self-contained). # # Build pipeline. scripts/build-from-upstream.sh, whe

What it can replace

Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.

Blynk logoBlynk ↗

Editorial workflow alternative: HTTPS/WebSocket telemetry dashboards, widgets and two-way control for hardware you already own; no hardware supply or managed-cloud parity.

See supporting source ↗
Ubidots logoUbidots ↗

Editorial workflow alternative: Self-managed telemetry variables, time-series dashboards and visual automations; no managed-service SLA or MQTT compatibility claim.

See supporting source ↗
external SaaS target
varies
external SaaS target
varies

How it works

The shape of nodrix on Cloudflare, and how it stacks up against the rented tools it replaces.

Architecture

Diagram of deployment declarations at the reviewed commit. Each app has its own entrypoint; declared resources do not prove runtime calls. Follow file and line sources below.

View upstream source ↗

Configuration and workflow sources

Reviewed commit 8fd634b3bf16. Files were read as data; upstream applications and CI jobs were not executed.

Partial source coverage: 58 files outside collection bounds; 0 collection or parsing issues. Dynamic imports and generated entrypoints may need manual review.

Deployment configuration · 2 files
wrangler.toml ↗

Cloudflare Workers · compatibility 2025-05-01

nodrix · default

Entrypoint: worker/src/index.ts

Build: [ -f scripts/build-from-upstream.sh ] || cd ..; bash scripts/build-from-upstream.sh

Static assets: web/dist · single-page-application · Worker first: true

Cron triggers (UTC): 0 0 * * *

  • DB → D1
  • KV → KV
  • R2 → R2
  • PROJECT_DO → Durable Objects · class ProjectDO
  • DASHBOARD_DO → Durable Objects · class DashboardDO
  • SCHEDULER_DO → Durable Objects · class SchedulerDO
  • MCP_OBJECT → Durable Objects · class NodrixMcpAgent
  • PROVISION → Workflows · class Provision
  • ASSETS → Static assets
deploy/wrangler.toml ↗

Cloudflare Workers · compatibility 2025-05-01

nodrix · default

Entrypoint: worker/src/index.ts

Build: curl -fsSL https://raw.githubusercontent.com/decoded-cipher/nodrix/master/scripts/build-from-upstream.sh | bash

Static assets: web/dist · single-page-application · Worker first: true

Cron triggers (UTC): 0 0 * * *

  • DB → D1
  • KV → KV
  • R2 → R2
  • PROJECT_DO → Durable Objects · class ProjectDO
  • DASHBOARD_DO → Durable Objects · class DashboardDO
  • SCHEDULER_DO → Durable Objects · class SchedulerDO
  • MCP_OBJECT → Durable Objects · class NodrixMcpAgent
  • PROVISION → Workflows · class Provision
  • ASSETS → Static assets

Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.

Runtime source · handlers, binding usage and workflow steps

Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.

worker/src/index.ts ↗
  • L50 · fetch handler exported · references KV · calls oauthProvider.fetch
  • L55 · scheduled handler exported · calls sendHeartbeat

Environment references: env.KV

worker/src/app.ts ↗
  • L12 · app.use("*")
  • L15 · app.use("*")

Environment references: c.env.DB

worker/src/mcp/flags.ts ↗
  • L16 · mcpEnabled calls (conditional paths may differ): getSetting
  • L20 · mcpWriteEnabled calls (conditional paths may differ): getSetting
worker/src/platform/lib/usage-stats.ts ↗
  • L35 · getOrCreateInstanceId calls (conditional paths may differ): getSetting, nanoid, setSetting
  • L44 · buildPayload calls (conditional paths may differ): first, env.DB.prepare, Object.entries, Number, all, filter, results.map, VALID_KINDS.has, getSetting
  • L80 · post calls (conditional paths may differ): fetch, JSON.stringify
  • L92 · sendInstall calls (conditional paths may differ): ctx.waitUntil, catch, getOrCreateInstanceId, post, buildPayload
  • L102 · sendHeartbeat calls (conditional paths may differ): ctx.waitUntil, catch, getSetting, post, buildPayload, first, env.DB.prepare, getOrCreateInstanceId

Environment references: env.DB · env.NODRIX_USAGE_STATS_URL

worker/src/platform/durable-objects/project-do.ts ↗
  • L687 · safeParse calls (conditional paths may differ): JSON.parse
  • L695 · hourBucket calls (conditional paths may differ): d.getUTCFullYear, padStart, String, d.getUTCMonth, d.getUTCDate, d.getUTCHours

Environment references: env.DB · env.R2

worker/src/platform/durable-objects/scheduler-do.ts ↗
  • L66 · soonest calls (conditional paths may differ): Math.min
  • L80 · stub calls (conditional paths may differ): env.SCHEDULER_DO.get, env.SCHEDULER_DO.idFromName
  • L85 · rescheduleScheduler calls (conditional paths may differ): reschedule, stub, console.error
  • L89 · ensureScheduler calls (conditional paths may differ): ensure, stub, console.error
  • L93 · armSchedulerFor calls (conditional paths may differ): armFor, stub, console.error

Environment references: env.SCHEDULER_DO

worker/src/platform/workflows/provision.ts ↗
  • L16 · Provision extends WorkflowEntrypoint
  • L18 · step.do("run-migrations")
  • L23 · step.do("sanity-check-r2")
  • L29 · step.do("sanity-check-kv")

Environment references: env.DB · env.R2 · env.KV

worker/src/platform/db/auto-migrate.ts ↗
  • L17 · ensureMigrated calls (conditional paths may differ): catch, applyMigrations, console.error
  • L28 · applyMigrations calls (conditional paths may differ): run, db.prepare, all, map, res.results.filter, bind, applied.has, console.log, Math.floor, Date.now
worker/src/routes.ts ↗
  • L45 · registerRoutes calls (conditional paths may differ): app.get, c.json, first, c.env.DB.prepare, app.route, app.all, serveDashboardSeo, c.req.param, c.env.ASSETS.fetch

Environment references: c.env.DB · c.env.ASSETS

worker/src/mcp/gate.ts ↗
  • L24 · authenticateMcp calls (conditional paths may differ): mcpEnabled, json, extractBearer, unauthorized, lookupUserToken
  • L43 · touchToken calls (conditional paths may differ): touchTokenLastUsed
  • L52 · mcpBearerHandler calls (conditional paths may differ): authenticateMcp, c.executionCtx.waitUntil, touchToken, mcpHandler.fetch
  • L60 · json calls (conditional paths may differ): JSON.stringify
  • L67 · unauthorized calls (conditional paths may differ): JSON.stringify
worker/src/mcp/tools-read.ts ↗
  • L42 · registerReadTools calls (conditional paths may differ): server.registerTool, run, getProject, listAccessibleProjects, actorOf, project.optional, resolveProjectId, listVariables, getState, describe, z.string, optional, regex, getSeries, listDashboards, getDashboard, Array.isArray, TRIGGER_CATALOG.map, CONDITION_CATALOG.map, ACTION_CATALOG.map
worker/src/mcp/tools-write.ts ↗
  • L29 · registerWriteTools calls (conditional paths may differ): actorOf, server.registerTool, z.string, run, createProject, actor, optional, nullable, updateProject, scopeProjectId, createVariable, updateVariable, describe, z.any, setVariableControl, createDashboard, parseStructured, z.number, updateDashboard, z.enum
worker/src/mcp/resources.ts ↗
  • L13 · registerResources calls (conditional paths may differ): getProject, listAccessibleProjects, actorOf, server.registerResource, map, accessibleProjects, resolveProjectId, String, getState, JSON.stringify, listVariables
worker/src/platform/lib/deployment-settings.ts ↗
  • L15 · getSetting calls (conditional paths may differ): env.KV.get, first, bind, env.DB.prepare, env.KV.put
  • L45 · setSetting calls (conditional paths may differ): Math.floor, Date.now, run, bind, env.DB.prepare, env.KV.delete

Environment references: env.KV · env.DB

worker/src/platform/lib/ids.ts ↗
  • L31 · newId calls (conditional paths may differ): nanoid
  • L37 · newToken calls (conditional paths may differ): crypto.getRandomValues, base64url
  • L44 · shareToken calls (conditional paths may differ): nanoid
  • L48 · sha256Hex calls (conditional paths may differ): crypto.subtle.digest, encode, join, map, padStart, b.toString
  • L53 · base64url calls (conditional paths may differ): String.fromCharCode, replace, replaceAll, btoa
worker/src/platform/durable-objects/stubs.ts ↗
  • L9 · projectStub calls (conditional paths may differ): env.PROJECT_DO.get, env.PROJECT_DO.idFromName
  • L13 · dashboardStub calls (conditional paths may differ): env.DASHBOARD_DO.get, env.DASHBOARD_DO.idFromName

Environment references: env.PROJECT_DO · env.DASHBOARD_DO

worker/src/platform/engine/run.ts ↗
  • L40 · runAutomation calls (conditional paths may differ): toGraph, nodesById, byId.get, entryNode, Number, defaultSetVariable, defaultEmitEvent, defaultGetVariable, defaultScheduleDelay, countActionNodes, visited.has, visited.add, delaySeconds, scheduleDelay, Date.now, VALID_ACTION_KINDS.has, runActionNode, VALID_CONDITION_KINDS.has, evalCondition, outgoingEdges
  • L127 · dispatchEvent calls (conditional paths may differ): all, bind, env.DB.prepare, triggerNodes, toGraph, Math.floor, Date.now, runAutomation, defaultEmitEvent
  • L166 · delaySeconds calls (conditional paths may differ): Number, String, Number.isFinite, Math.min, Math.max, Math.round
  • L173 · defaultScheduleDelay calls (conditional paths may differ): first, bind, env.DB.prepare, run, newId, JSON.stringify, Math.floor, Date.now, armSchedulerFor
  • L196 · defaultSetVariable calls (conditional paths may differ): addControl, projectStub, newId
  • L202 · defaultEmitEvent calls (conditional paths may differ): dispatchEvent
  • L208 · defaultGetVariable calls (conditional paths may differ): getLatestState, projectStub, rows.find
  • L215 · recordRun calls (conditional paths may differ): Math.floor, Date.now, run, bind, env.DB.prepare

Environment references: env.DB

worker/src/platform/engine/triggers.ts ↗
  • L8 · matchVariableCondition calls (conditional paths may differ): valuesEqual, evalOp
  • L27 · evalOp calls (conditional paths may differ): looseEqual, toNum
  • L41 · toNum calls (conditional paths may differ): Number, Number.isNaN
  • L47 · looseEqual calls (conditional paths may differ): Boolean, toNum, String
  • L57 · valuesEqual calls (conditional paths may differ): looseEqual
worker/src/platform/engine/graph.ts ↗
  • L17 · toGraph calls (conditional paths may differ): JSON.parse, isGraph
Build and deployment pipeline · 1 GitHub Actions workflows

Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.

CodeQL Advanced · .github/workflows/codeql.yml ↗

Triggers: workflow_dispatch, schedule

Analyze (${{ matrix.language }}) · no job dependencies declared

  1. Checkout repositoryactions/checkout@v4
  2. Initialize CodeQLgithub/codeql-action/init@v4
  3. Perform CodeQL Analysisgithub/codeql-action/analyze@v4
package.json ↗
  • build:version: bun scripts/gen-version.ts
  • build:migrations: bun scripts/gen-migrations.ts
  • build: bun run build:version && bun run build:migrations && bun run --filter @nodrix/web build && bun run --filter @nodrix/worker build
  • deploy:platform: bun run build && bun run --filter @nodrix/worker deploy
web/package.json ↗
  • build: vue-tsc --noEmit && vite build
worker/package.json ↗
  • build: wrangler deploy --config ../wrangler.toml --dry-run --outdir=dist
  • deploy: wrangler deploy --config ../wrangler.toml

Full upstream document by @decoded-cipher · README.md · snapshot 8fd634b

nodrix

The IoT platform Cloudflare didn't build. Point your hardware at one endpoint over HTTPS or WebSocket, watch variables appear on their own, build realtime drag-and-drop dashboards, automate, and read it all back through a clean API — entirely on infrastructure you own. nodrix is single-tenant and open source: it deploys into your Cloudflare account on Workers, Durable Objects, D1, and R2.

Features

  • 📡 Telemetry over HTTPS or WebSocket — hardware POSTs JSON to a project; variables auto-create on first sight. No schema to define, no MQTT broker to run.
  • 📊 Realtime dashboards — a drag-and-drop widget grid streams updates over hibernating WebSockets; share any dashboard read-only by public link.
  • 🧩 Embeddable widgets — every widget is a framework-agnostic Web Component you can lift straight into your own app.
  • 🎮 Two-way control — toggles, sliders, color pickers, and buttons write values back to hardware via short polls or a control socket.
  • 🤖 Visual automations — variable, schedule, sunrise/sunset, and event triggers run conditions and actions: webhooks, code snippets, and service integrations.
  • 🔌 Integrations — fan out to HTTP, email, and chat (Slack, Telegram, Discord, and more).
  • 📖 Clean read API — latest state, time-series, and variable listings behind one token.
  • 🧠 Native MCP server — read telemetry and control hardware from an AI assistant, on devices you are not physically next to. Owner-gated and off by default; see AI assistants (MCP).
  • 👥 Multi-user — owner / admin / member roles, email invites, and social sign-in (Google, GitHub).
  • 📝 Audit log — every privileged action recorded and paginated in the UI.

Quick start

  1. Deploy to your Cloudflare account — one click, or bun run deploy:platform from a clone.

  2. Create the owner account — the first visit prompts a "Create owner account" page; the first signup becomes owner.

  3. Create a project and mint a project token from the dashboard.

  4. Send telemetry — variables are created the moment data arrives:

    curl -X POST https://<your-worker>/v1/telemetry \
      -H "Authorization: Bearer $NODRIX_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"metrics":{"temperature":23.4,"humidity":61}}'
    
  5. Read it back:

    curl https://<your-worker>/v1/projects/<project>/state \
      -H "Authorization: Bearer $NODRIX_TOKEN"
    

AI assistants (MCP)

nodrix ships a native Model Context Protocol server, so an assistant can read a project's telemetry and — once you allow it — act on the hardware. Because devices report to your deployment rather than to your laptop, the board does not need to be plugged into the machine running the assistant; it can be in another building.

Two endpoints, both Streamable HTTP:

Endpoint Auth For
/v1/mcp Bearer token CLI and IDE clients
/v1/mcp/oauth OAuth claude.ai-style connectors

Both are off by default. The owner flips them in Settings → More:

  • mcp_enabled — the master switch. While it is off, /v1/mcp returns 404, so a disabled server looks absent rather than merely forbidden.
  • mcp_write_enabled — gates the management and control tools. Until it is on, even an admin-scope token gets read-only tools, so an assistant cannot command hardware by default.

12 read tools — list_projects, list_variables, get_state, get_series, list_dashboards, get_dashboard, list_widget_types, list_widgets, list_block_types, list_integration_kinds, list_automations, list_integrations.

16 write tools — creating and updating projects, variables, dashboards, widgets, automations, and integrations, plus set_variable (how an assistant turns a relay on), run_automation, emit_event, and test_integration.

There are no delete tools, by design. Every tool resolves its target project through the token's scope before it runs, so it cannot reach a project the token cannot.

Architecture

  • Worker (worker/) — a single Hono app. Durable Objects for Project, Dashboard, Scheduler, and the MCP agent; one Workflow for provisioning; D1 (metadata), R2 (telemetry history), KV (read cache + JWKS).
  • Web (web/) — Vue 3 + Tailwind + Reka UI admin panel and drag-and-drop dashboard builder, built and served as Worker static assets.
  • Shared (shared/) — framework-agnostic Web Component widgets, the integration catalog, and automation blocks, consumed by both web and worker so there is a single source of truth.
  • Deploy (deploy/) — the small config carrier behind the one-click Deploy to Cloudflare.
worker/   Cloudflare Worker — API, Durable Objects, Workflow
web/      Vue 3 admin panel + dashboard builder
shared/   Web Component widgets, integration catalog, automation blocks
deploy/   One-click Deploy to Cloudflare config
scripts/  Build, version, and migration generators

Storage

Store Holds
Project DO (SQLite) Latest variable state, recent ring buffer, pending control writes, flush cursor
R2 Cold telemetry history (NDJSON, partitioned by project + hour)
D1 Users, sessions, accounts, projects, variables, dashboards, tokens, automations, integrations, audit log, OAuth provider config (metadata only — never any telemetry point)
KV Cached /state responses and JWKS
Dashboard DO Per-dashboard subscriptions + hibernated WebSockets
Scheduler DO One alarm at the next schedule/sunset automation fire time

Authentication

Better Auth handles sign-in. Email + password is on by default; Google and GitHub OAuth can be enabled at runtime from Settings → Sign-in providers (the owner enters a client ID + secret per provider, and the login page shows the matching buttons immediately).

The first signup on a fresh deployment becomes owner. After that, registration is closed: the owner invites people from Users, each with an owner / admin / member role. Sessions are cookie-based and persist 30 days; each device is a separate session, listed and revokable from Users.

Development

nodrix uses Bun.

bun install
bun run dev              # worker (wrangler dev)
bun run dev:web          # web (vite)
bun run typecheck
bun run build
bun run deploy:platform  # build + deploy the worker

License

MIT © Arjun Krishna

Frequently asked about nodrix

What is nodrix?+

nodrix is a self-hosted Blynk/Ubidots alternative built on the Cloudflare developer platform. Self-managed IoT telemetry, realtime dashboards and device control on Cloudflare.

What does nodrix replace?+

nodrix is listed as an alternative to Blynk, Ubidots. Compare the features and tradeoffs before migrating.

What Cloudflare primitives does nodrix use?+

nodrix is built on D1, Durable Objects, KV, R2, Workers, Workflows.

How much does nodrix cost to run?+

The reviewed Cloudflare deployment is eligible for Free-plan allowances for the stated small workload and feature scope. Usage limits, CPU, required account setup and separate services apply. Provide your own physical hardware and network connection; Cloudflare runs the telemetry/control platform. Use the pinned repository build with explicitly provisioned D1/KV/R2 IDs; the one-click carrier otherwise pulls a moving upstream revision. Keep Worker requests/10 ms CPU, D1/KV/R2 and SQLite DO requests/duration/storage within free quotas; low telemetry frequency and bounded history are essential. Provisioning Workflow shares Worker request/CPU quotas and includes 3,000 steps/day plus 1 GB-month state on Free; retained instance state counts. Optional email, chat and OAuth integrations have separate provider accounts/costs; core owner password setup does not require them. Workers Free dynamic requests are shared across this account (100,000/day), with 10 ms CPU per invocation; workload fit is conditional and has not been measured. D1 Free allowance: 5 million rows read/day, 100,000 rows written/day and 5 GB total storage; unindexed scans and history retention consume quota. KV Free allowance: 100,000 keys read/day and 1,000 each writes/deletes/list requests/day; this may constrain updates before Worker request limits. R2 Standard allowance: 10 GB-month storage, 1 million Class A and 10 million Class B operations/month; account activation may require billing setup, and other storage classes are excluded. Only SQLite Durable Objects qualify for Workers Free. Keep DO requests below 100,000/day, active duration below 13,000 GB-s/day and SQLite storage/operations inside the captured allowances. Check current Cloudflare pricing before deploying.

Is nodrix open source?+

The upstream repository declares the MIT license. Read its terms at https://raw.githubusercontent.com/decoded-cipher/nodrix/8fd634b3bf166635f2212907904a7a8dd4972381/LICENSE. Source code and contributor credit are available at https://github.com/decoded-cipher/nodrix.

Discussion · 0

sign in to comment →
No comments yet — be the first.