Skip to content

Trebired/dev-mode

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@trebired/dev-mode

Offline developer-mode activation for Bun and Node.js applications.

@trebired/dev-mode gives products a small local runtime for turning on a custom developer mode without calling any remote service. It supports two activation styles:

  • match, where one local value must match another local value
  • signed, where a local activation payload is verified against shipped public key material

The package is intentionally narrow. It helps with offline activation and stable runtime checks. It does not try to be a licensing platform, a cloud gate, or a hidden anti-tamper system.

Install

Runtime support: Bun 1+ and Node.js 18+.

npm install @trebired/dev-mode

Quick Start

import { createDevMode } from "@trebired/dev-mode";

const devMode = createDevMode({
  mode: "match",
  match: {
    env: { env: "DEV_MODE_KEY" },
    match: { file: ".dev-mode-key" },
  },
});

const state = devMode.activate();

if (devMode.isEnabled()) {
  console.log("developer mode active");
}

console.log(state);

The instance API is intentionally small:

  • activate()
  • isEnabled()
  • isMatchMode()
  • isSignedMode()
  • state()

If you want a one-shot helper, use activateDevMode(options).

Match Mode

match mode is a local friction check. It is useful when you want a product to require two local values to line up before developer mode turns on.

import { createDevMode } from "@trebired/dev-mode";

const parsedEnvFile = {
  DEV_MODE_KEY: "local-demo-key",
};

const devMode = createDevMode({
  mode: "match",
  match: {
    env: { env: "DEV_MODE_KEY" },
    match: { key: "DEV_MODE_KEY", object: parsedEnvFile },
  },
});

const state = devMode.activate();

Supported local sources for match.env and match.match:

  • direct values such as "local-demo-key"
  • environment objects through { env: "DEV_MODE_KEY" }
  • parsed records through { key: "DEV_MODE_KEY", object: parsedEnvFile }
  • local files through { file: "./dev-mode-key.txt" }

String values are normalized with trimming by default. Activation only succeeds when both normalized values are present and equal.

Important: match mode is not a security boundary. It is a friction mechanism.

Signed Mode

signed mode is stronger than shared-secret matching because the shipped product only needs public key material. A private key can stay elsewhere and produce local activation envelopes ahead of time.

The package verifies an embedded activation envelope of this shape:

{
  "payload": {
    "subject": "local-developer-mode",
    "issued_at": "2026-05-23T00:00:00.000Z",
    "expires_at": "2026-06-01T00:00:00.000Z",
    "device": "workstation-17",
    "metadata": {
      "issued_by": "local-admin"
    }
  },
  "signature": "<base64-ed25519-signature>"
}

Runtime verification is local only:

import { createDevMode } from "@trebired/dev-mode";

const devMode = createDevMode({
  mode: "signed",
  signed: {
    activation: { file: "./activation.json" },
    publicKey: { file: "./keys/dev-mode-public.pem" },
  },
});

const state = devMode.activate();

If you want a local signing script elsewhere, sign the deterministic payload serialization that this package exports:

import fs from "node:fs";
import { sign } from "node:crypto";

import { serializeSignedPayload } from "@trebired/dev-mode";

const privateKey = fs.readFileSync("./keys/dev-mode-private.pem", "utf8");

const payload = {
  subject: "local-developer-mode",
  issued_at: "2026-05-23T00:00:00.000Z",
  expires_at: "2026-06-01T00:00:00.000Z",
  device: "workstation-17",
};

const signature = sign(
  null,
  Buffer.from(serializeSignedPayload(payload)),
  privateKey,
).toString("base64");

fs.writeFileSync("./activation.json", JSON.stringify({
  payload,
  signature,
}, null, 2));

Signed mode supports activation input from:

  • direct activation objects
  • local JSON strings
  • local JSON files

The public key can come from:

  • a PEM string
  • a KeyObject
  • a local PEM file

NODE_ENV Override

By default, successful activation sets NODE_ENV to "development".

The override policy is controlled with overrideNodeEnv:

  • "always": always set NODE_ENV="development" on successful activation
  • "when-unset": only set it when NODE_ENV is missing or blank
  • "never": never modify NODE_ENV

The state snapshot always includes:

  • active
  • mode
  • node_env_overridden
  • override_node_env
  • reason

Mode-specific flags are added when relevant, including:

  • env_key_present
  • match_key_present
  • payload_present
  • signature_present
  • public_key_present
  • expires_at
  • expired

What This Package Is

  • an offline developer-mode activation helper
  • a small runtime state wrapper around local activation checks
  • a way to use public-key verification instead of shipping a shared secret

What This Package Is Not

  • not DRM
  • not a remote licensing platform
  • not a perfect anti-tamper system
  • not a replacement for host-owned permissions, trust boundaries, or server-side enforcement

Security Model And Limitations

match mode is intentionally simple. If a user can inspect local files or environment values, they can usually reproduce the matching value. That is expected.

signed mode is stronger because the shipped product only needs a public key and verifies signatures locally. It avoids embedding a reusable shared secret in the product. Even so, if code runs on a user-controlled machine, the code and checks can still be patched, bypassed, or instrumented.

In plain terms:

  • match mode is friction, not security
  • signed mode is stronger, but still not absolute tamper resistance
  • this package is for offline mode gating and developer-mode control
  • this package is not a perfect anti-tamper system

Current API

The first public slice is intentionally small:

  • createDevMode()
  • activateDevMode()
  • serializeSignedPayload()
  • DevModeError

The package also exports the public TypeScript types for mode options, runtime state, logger hooks, and signed activation payloads.

About

Offline developer-mode activation for Bun and Node.js applications.

Resources

License

Stars

Watchers

Forks

Contributors