Freshet

Logo

Thaw any JSON URL into a more useful page. Liquid templates per URL pattern — fields surfaced, statuses colored, IDs linkified.

View the Project on GitHub MattAltermatt/freshet

Sharing rules and templates

Freshet exports and imports rules + templates as a single plain-JSON file with the extension .freshet.json. Everything is human-readable — you can open a bundle in any editor, diff it, and audit it before importing. This page is the reference.

Bundle format

A bundle is a single JSON object with bundleSchemaVersion: 1. Any other value is rejected at the parse step — no loose compatibility shims.

Top-level fields

Field Type Required Purpose
bundleSchemaVersion number (literal 1) Schema version. Must be 1.
exportedAt string (ISO-8601) Timestamp the bundle was produced.
exportedBy string optional Free-form origin label (e.g. app name).
appVersion string Freshet version that produced the bundle.
templates BundleTemplate[] Templates included in the bundle (may be empty).
rules BundleRule[] Rules included in the bundle (may be empty).

BundleTemplate

Field Type Required Purpose
name string Template name. Must be unique within the bundle.
source string Liquid template source (HTML + {{ }} / {% %}).
sampleJson string optional Per-template sample JSON shown in the editor preview.

BundleRule

Field Type Required Purpose
id string Stable rule identifier.
name string optional Human-readable label shown on the rule card.
hostPattern string Glob or /regex/ matched against the URL host.
pathPattern string Glob or /regex/ matched against the URL pathname.
templateName string Must match a templates[].name in the same bundle.
variables Record<string, string> optional Per-rule template variables.
active boolean Whether the rule is on.

Example

{
  "bundleSchemaVersion": 1,
  "exportedAt": "2026-04-19T20:45:00.000Z",
  "exportedBy": "Freshet",
  "appVersion": "1.0.0",
  "templates": [
    {
      "name": "service-health",
      "source": "<h1>{{ service.name }} — {{ service.status }}</h1>",
      "sampleJson": "{\"service\":{\"name\":\"payments\",\"status\":\"ok\"}}"
    }
  ],
  "rules": [
    {
      "id": "rule-service-health",
      "name": "Service health",
      "hostPattern": "status.example.com",
      "pathPattern": "/api/services/**",
      "templateName": "service-health",
      "variables": { "env": "prod" },
      "active": true
    }
  ]
}

Secret-sniff patterns

Every bundle is scanned on both ends — at export (so you know what you’re about to send) and at import (so you know what you’re about to accept). Hits are surfaced with the literal pattern that matched and the literal matched text. Nothing is auto-redacted; you decide.

Scanned fields:

Pattern ID Kind Regex What it catches
KEY_SECRETY key /token\|secret\|key\|password\|auth\|bearer\|api[_-]?key\|credential/i Field/variable names that look like they hold credentials.
BEARER_PREFIX value /^Bearer\s+\S+/ OAuth/HTTP bearer tokens.
JWT value /^eyJ[\w-]+\.[\w-]+\.[\w-]+$/ Three-part JWTs (base64url header + payload + signature).
OAUTH_GOOGLE value /^ya29\./ Google OAuth access tokens.

The patterns themselves live in src/bundle/sniff.ts. If you hit a false positive or want more coverage, open an issue — detection should be easy to reason about, not a guessing game.

Collision resolution

On import, Freshet compares the incoming bundle against your current rules and templates and classifies each item:

Template collisions

Two templates with the same name. Resolution options:

Rule collisions

Rules collide in three different ways:

“Just append all”

If you don’t want to review each item, the importer offers a one-click append path that:

Imported rules are inactive by default

Every rule that lands via import is written with active: false, regardless of the active value in the bundle. You flip the toggle on the rule card once you have reviewed it. The bundle still serializes the original active state — exports are faithful; imports are cautious.

Atomic commit

The full import is committed in one transaction across four storage keys (templates, pj_sample_json, rules, pj_import_flags). Any failure rolls back to the pre-import snapshot — partial imports never land.

Warn, don’t block

Freshet’s security posture for sharing is deliberate:

What’s never shared

The bundle format contains only the fields listed under Bundle format. These storage keys and fields are intentionally kept out:

If you want to share UI preferences or skip lists, do it out-of-band. Bundles are for rules and templates.