Secrets & Security

Storing your write token safely

Your EasyBoard write token is the key to modifying your dashboard. Where and how you store it depends on your setup — this guide explains the options and helps you choose the right one.

The one rule

Never commit a write token to a public Git repository. It's not a catastrophe if someone gets yours — they can update tiles, but can't access your account or other dashboards. Still, if it leaks, regenerate it immediately from your dashboard settings.

Which approach is right for you?

Device only you use, in your own space

Config file or NVS

Simple, works well. Keep the file out of version control.

Device in a shared or public space

NVS on ESP32, env var on Pi

Credentials aren't visible in code files. Physically harder to extract.

Multiple devices sharing one dashboard

One token per device (if possible) or proxy pattern

If one device's token is compromised, you can regenerate just that one.

Devices deployed to end users or customers

Proxy server

Token never leaves your infrastructure. Required for any commercial deployment.

Option 1 — Config file (simplest)

The easiest approach: put credentials in a separate file and add that filename to .gitignore. Your code imports from this file; the file itself never gets committed.

For Arduino/ESP32, this is the config.h pattern described in the ESP32 guide. For Python scripts, use a .env file:

.env
# .env  ← add to .gitignore
EASYBOARD_DASHBOARD_ID=abc123xyz456
EASYBOARD_WRITE_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Python
import os
from dotenv import load_dotenv  # pip install python-dotenv

load_dotenv()

DASHBOARD_ID = os.environ["EASYBOARD_DASHBOARD_ID"]
WRITE_TOKEN  = os.environ["EASYBOARD_WRITE_TOKEN"]

Add .env to .gitignore right now

Run echo ".env" >> .gitignore before you create the file. It's easy to forget and commit it by accident if you add the gitignore entry later.

Option 2 — NVS storage on ESP32

NVS (Non-Volatile Storage) is a small key-value store built into every ESP32's flash memory. It survives reboots, power cycles, and firmware updates (unless you explicitly erase it). Credentials stored in NVS aren't visible in your code files — they're a step harder to extract than a plain config file.

The setup is two sketches: one to write the token once, then your main sketch that reads it.

First: run this once to store the token

store_credentials.ino
// Run this sketch once to store your token in flash.
// After it runs, replace this sketch with your main one.
// The token survives reboots and power cycles.

#include <Preferences.h>

void setup() {
  Serial.begin(115200);
  Preferences prefs;

  prefs.begin("easyboard", false);           // open namespace for writing
  prefs.putString("token", "your-write-token-here");
  prefs.putString("dashId", "your-dashboard-id");
  prefs.end();

  Serial.println("Token stored. Upload your main sketch now.");
}

void loop() {}

Then: read from NVS in your main sketch

C++
// In your main sketch — read the token from flash at startup.
#include <Preferences.h>

String writeToken;
String dashboardId;

void setup() {
  Serial.begin(115200);

  Preferences prefs;
  prefs.begin("easyboard", true);            // true = read-only
  writeToken   = prefs.getString("token", "");
  dashboardId  = prefs.getString("dashId", "");
  prefs.end();

  if (writeToken.isEmpty() || dashboardId.isEmpty()) {
    Serial.println("ERROR: No credentials stored. Run the setup sketch first.");
    while (true) delay(1000);               // halt
  }

  // ... rest of setup: WiFi, sensors, etc.
}

What about token rotation?

If you regenerate your write token from the EasyBoard dashboard, you need to re-run the setup sketch on each device. For a handful of devices, this is manageable. For many devices, consider the proxy pattern below — you only update credentials in one place.

Option 3 — Proxy server (most secure)

With a proxy, your devices send readings to a small server you control (a Raspberry Pi, a cheap VPS, or a serverless function). That server holds the EasyBoard write token and forwards the data. The token never touches the devices themselves.

ESP32 / sensor

No token stored

Your proxy server

Holds the write token

EasyBoard

Dashboard updates live

Here's a minimal proxy using Python and Flask — about 30 lines:

proxy.py
# proxy.py — runs on a Raspberry Pi, VPS, or any server
# Devices POST their readings here; this script forwards to EasyBoard.
# The write token never leaves this server.
#
# Install: pip install flask requests
# Run:     python proxy.py

import os
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)

DASHBOARD_ID = os.environ["EASYBOARD_DASHBOARD_ID"]
WRITE_TOKEN  = os.environ["EASYBOARD_WRITE_TOKEN"]
BASE_URL     = f"https://easyboard.live/api/d/{DASHBOARD_ID}/tiles"

@app.route("/update/<tile_id>", methods=["POST"])
def update(tile_id):
    data = request.get_json()
    value = str(data.get("value", ""))

    resp = requests.patch(
        f"{BASE_URL}/{tile_id}",
        headers={"Authorization": f"Bearer {WRITE_TOKEN}"},
        json={"value": value},
        timeout=10,
    )
    return jsonify({"status": resp.status_code}), resp.status_code

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

And the matching ESP32 code to call it:

C++
// ESP32 calls your proxy instead of EasyBoard directly.
// The write token is never on the device.

void updateViaProxy(const char* tileId, const String& value) {
  if (WiFi.status() != WL_CONNECTED) return;

  HTTPClient http;
  // Your proxy server's local IP or hostname
  String url = "http://192.168.1.100:5000/update/" + String(tileId);

  http.begin(url);
  http.addHeader("Content-Type", "application/json");

  StaticJsonDocument<64> doc;
  doc["value"] = value;
  String body;
  serializeJson(doc, body);

  http.POST(body);  // note: POST to proxy, not PATCH
  http.end();
}

Running the proxy on a Raspberry Pi?

Use a systemd service to keep it running after reboots. Set the environment variables in the service file under [Service] as Environment=EASYBOARD_WRITE_TOKEN=... — this keeps them out of your code and out of version control.