npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sealed-api-gateway/console

v1.0.2

Published

A password-gated, Network-tab-style console for reading the traffic @sealed-api-gateway/core seals.

Readme

@sealed-api-gateway/console

Read the traffic the gateway hid — a Network tab for calls the Network tab can no longer show.

npm License: MIT Next.js React MUI


What it does

@sealed-api-gateway/core turns every API call into one opaque POST. That is the point — and it also means DevTools can no longer help you debug.

This puts the plaintext back, behind a password:

| | | | --- | --- | | Request list | Status, method, name, page, tab, size, time | | Detail pane | Headers · Params · Payload · Response · Timing | | JSON tree | Folding, line-numbered, searchable | | Binary | Inline preview, hex dump, download | | Copy as cURL | Against the real backend, so a failing call goes straight to a backend engineer | | Analysis | Status distribution, size and duration over time, p95 and error-rate tiles |

[!IMPORTANT] Unset the password and the console does not exist. With no SECURE_GATEWAY_PASSWORD, the component renders null, the unlock endpoint 404s, and nothing is recorded anywhere. That is the off switch, and it is the default.


Contents


Install

npm i @sealed-api-gateway/console

Peers: @sealed-api-gateway/core (≥ 0.3.0), next, react, @mui/material, @mui/icons-material, @emotion/react, @emotion/styled.

If you already render with MUI, this package is the only new thing.


Quick start

One component and one environment variable. No route file, no middleware change.

1. Mount it beside the gateway

// app/layout.tsx
import { SecureApiGateway } from '@sealed-api-gateway/core';
import { GatewayConsole } from '@sealed-api-gateway/console';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <SecureApiGateway />
        <GatewayConsole />
        {children}
      </body>
    </html>
  );
}

2. Set a password

# .env
SECURE_GATEWAY_PASSWORD=pick-something-long

3. Make sure the gateway route exists

npx sealed-gateway-init

The console adds no route of its own — but it reaches the gateway over HTTP for its handshake and unlock, so the gateway's route has to be mounted.

[!IMPORTANT] From @sealed-api-gateway/core 1.0 the gateway itself no longer needs that file — it uses Server Actions by default. If you installed core alone and skipped sealed-gateway-init, run it now, or the console will sit locked with its unlock endpoint returning 404.

This is the console's one outstanding rough edge, and it is temporary: moving it onto core's Server Actions is the next change, and will not alter this package's API.

4. Restart and press Ctrl/Cmd + Shift + D

[!NOTE] The console renders MUI components, so it must sit inside whatever provides your theme. If your ThemeProvider lives below the layout root, mount <GatewayConsole /> inside it instead.

Earlier versions needed app/gw-debug/page.tsx and app/gw-debug/api/unlock/route.ts. A console rendered as a route needs route files, and Next derives routes from the filesystem — no package can create one.

So it no longer renders as a route. It is an overlay, mounted from your layout, and its unlock endpoint hangs off the gateway's route, which you had to create anyway. Two files became zero.

That also removes the old middleware note: there is no /gw-debug path for auth middleware to intercept, so nothing needs excluding.


Opening it

| | | | --- | --- | | Ctrl/Cmd + Shift + D | Toggles the overlay | | #gw-debug in the URL | Opens it, survives a reload, and can be shared with a colleague | | Escape | Closes it |

There is deliberately no visible launcher button. The console hangs off every page of your app, and a floating button would sit in front of real users who cannot open it anyway.

Ctrl/Cmd + Shift + I, J and C are DevTools in every major browser and cannot be intercepted — which is why the default avoids them.


Everything you can change

Props on <GatewayConsole />

| Prop | Type | Default | What it changes | | --- | --- | --- | --- | | gatewayRoot | string | '/api/gw' | Where the gateway route is mounted. Must match <SecureApiGateway basePath>. | | hotkey | Hotkey \| null | Ctrl/Cmd + Shift + D | The shortcut. null disables it. | | hash | string \| null | 'gw-debug' | The URL hash that opens it. null disables it. | | colors | ConsoleThemeOptions | built-in palettes | Colour overrides → theming |

<GatewayConsole
  gatewayRoot="/api/proxy"
  hotkey={{ key: 'K', ctrlOrMeta: true, alt: true }}
  hash="inspector"
  colors={{ dark: { '--sg-ok': '#00d68f' } }}
/>

A Hotkey is { key, ctrlOrMeta?, shift?, alt? }. Modifiers you do not ask for must be absent, so Ctrl+Shift+D does not also fire on Ctrl+Shift+Alt+D.

Environment variables

The console adds none of its own. It reads two from the gateway:

| Variable | Effect | | --- | --- | | SECURE_GATEWAY_PASSWORD | Unset ⇒ console does not exist and nothing is recorded. Set ⇒ console available behind it. | | SECURE_GATEWAY_UNLOCK_TTL_HOURS | How long an unlock lasts. Default 8. |

Mounting it somewhere of your own

If you want the console inside an admin shell, a drawer or a tab rather than as an overlay, compose the pieces yourself:

import { DebugConsole, UnlockForm } from '@sealed-api-gateway/console';

const [unlocked, setUnlocked] = useState(false);

return unlocked
  ? <DebugConsole onClose={close} colors={colors} />
  : <UnlockForm onUnlocked={() => setUnlocked(true)} onClose={close} />;

Both accept gatewayRoot.

What you deliberately cannot change

| Fixed behaviour | Why | | --- | --- | | The log holds only your own browser's traffic | It is read from your browser's storage. There is no server-side log and no way to see another user's requests. | | Retention is 1 hour / 300 entries | A developer tool that grows without bound in every user's browser is a bug, not a feature. | | Binary bodies are capped (~3 MB in memory, ~110 KB shared across tabs) | So one screenshot cannot evict the whole log. | | Nothing is recorded without a password | The off switch has to be the default, or the tool becomes a liability. |


Theming

Every colour lives in one place, exported as data:

import { LIGHT, DARK } from '@sealed-api-gateway/console';

42 variables as light/dark pairs — surfaces, text, borders, chart marks, JSON syntax, search highlight. Override any of them by name; everything you do not name keeps its default:

<GatewayConsole
  colors={{
    dark:  { '--sg-ok': '#00d68f', '--sg-server': '#ff5c5c' },
    light: { '--sg-bg': '#ffffff' },
  }}
/>

[!NOTE] In the copy-the-folder version you edited theme.ts directly. Inside node_modules you cannot, so the palettes became exported data plus a merge. That is the one place packaging made this worse — and it is why the reference application still ships the console as a folder you own outright.

It does not read your theme, on purpose

The console cannot assume the app it is dropped into has a dark scheme, a CSS-variable theme, or a usefully configured palette — so it ships its own defaults and stays legible anywhere. MUI components inside it (buttons, chips, inputs) still follow your theme, so it never looks foreign.

It does follow your light/dark choice, resolved in three tiers:

| Tier | Used when | How | | --- | --- | --- | | 1 | theme.applyStyles exists (MUI 5.18+) | Dark values nested under your theme's own colour-scheme selector — the only form correct for a createTheme({ cssVariables, colorSchemes }) theme, where palette.mode reports the default mode rather than the active one | | 2 | Older themes | theme.palette.mode | | 3 | Opt-in | prefers-color-scheme, via colors={{ followOsWhenHostIsLight: true }} |


The password gate

| State | Behaviour | | --- | --- | | Password unset | Component renders null, unlock endpoint 404s, nothing recorded | | Wrong password | 401. Ten attempts per five minutes, then 429 | | Unlocked | httpOnly; SameSite=Strict cookie, 8 hours by default | | Password changed | Every outstanding unlock dies immediately |

The cookie is a self-verifying <expiry>.<hmac> pair keyed on the password itself. There is no session store to keep, and rotating the password is the revocation mechanism.

[!NOTE] What the password is actually for. The console shows a visitor their own browser's traffic — which they could already reach through DevTools. The password keeps the tool out of casual reach; it does not protect data from the person operating the browser. Nothing here can show another user's requests.


Compatibility

| | Supported | | --- | --- | | Next.js | 13.4 – 16, App Router | | React | 18.2 – 19 | | MUI | 5, 6, 7, 9 — there is no v8; the majors went 5 → 6 → 7 → 9 | | Node | ≥ 18.17 | | @sealed-api-gateway/core | ≥ 0.3.0 — earlier versions have no unlock endpoint |

Spanning MUI 5 through 9 takes care, because prop and slot APIs were renamed between majors and the old and new names are mutually exclusive: MUI 9 rejects PaperProps, MUI 5 does not know slotProps. The affected spots use composition and sx, which behave identically on every major.


Troubleshooting

| Symptom | Cause | Fix | | --- | --- | --- | | Hotkey does nothing | No password set, so the console is switched off | Set SECURE_GATEWAY_PASSWORD and restart | | Hotkey does nothing, password is set | The gateway route is missing or mounted elsewhere | npx sealed-gateway-init, or pass gatewayRoot | | Unlock returns 404, gateway itself works fine | Core 1.0+ runs on Server Actions, so you may never have created the route the console needs | npx sealed-gateway-init | | Console opens but is empty | No traffic captured yet | Trigger an API call — only gateway-routed origins appear | | Unlock succeeds, then the form returns | gatewayRoot disagrees with <SecureApiGateway basePath> | Make them match | | Overlay renders unstyled | Mounted outside your ThemeProvider | Move <GatewayConsole /> inside it | | Cannot find module '@sealed-api-gateway/core' | Peer not installed | npm i @sealed-api-gateway/core | | Unlock returns 404 | Core older than 0.3.0 | npm i @sealed-api-gateway/core@latest |



At a glance

| | | | --- | --- | | Request list with status, method, size, timing | ✅ | | Detail pane — Headers · Params · Payload · Response · Timing | ✅ | | Folding, line-numbered JSON tree | ✅ | | Binary preview with hex dump and download | ✅ | | Copy as cURL, against the real backend | ✅ | | Analysis — status mix, size and duration over time, p95, error rate | ✅ | | Filters shared across both views | ✅ | | Cross-tab log | ✅ | | Opens with a hotkey or a URL hash | ✅ | | Needs a route file of its own | ❌ none | | Shows other users' traffic | ❌ never |

FAQ

Can it show another user's requests? No. The log is read from your own browser's storage. There is no server-side log, and no mechanism by which one browser could see another's traffic.

What does the password actually protect? It keeps the tool out of casual reach. The console shows a visitor their own traffic — which they could already reach through DevTools — so the password is a lock on a convenience, not a barrier around secrets.

What happens with no password set? The component renders null, the unlock endpoint 404s, and nothing is recorded. That is the default, and it is why installing this is safe in production before you have decided whether to enable it.

How long is an unlock good for? Eight hours by default; set SECURE_GATEWAY_UNLOCK_TTL_HOURS to change it. Changing the password revokes every outstanding unlock immediately, because the cookie is keyed on the password itself.

Does it slow the app down? Only when open. While closed it is a single component that has made one handshake request and rendered nothing.

Can I restyle it? Every colour is an exported variable you can override by name — see Theming. If you expect to restyle heavily, the reference app ships the console as a folder you own outright.

Can I put it somewhere other than an overlay? Yes — compose DebugConsole and UnlockForm yourself. See mounting it somewhere of your own.


Design decisions

Why an overlay instead of a route? A console rendered as a route needs route files, and no package can create them — Next derives routes from the filesystem. As an overlay it needs none, so installing is npm i plus one component.

Why no visible launcher button? It hangs off every page of your app. A floating button would sit in front of real users, who cannot open it anyway.

Why Ctrl/Cmd + Shift + D? Ctrl+Shift+I, +J and +C are DevTools in every major browser and cannot be intercepted. A shortcut the browser eats is one nobody can use and nobody can debug.

Why does it not read colours from my theme? It cannot assume the app it is dropped into has a dark scheme, a CSS-variable theme, or a usefully configured palette. It ships its own defaults so it stays legible anywhere — while MUI components inside it still follow your theme, so it never looks foreign.

Why does the password gate live in the gateway package? So the console needs no server route. The gateway already owns one, so the unlock endpoint hangs off it.

Relationship to the reference app

Extracted from Sealed-API-Gateway, a full Next.js app demonstrating the gateway and console together.

| | Reference app | This package | | --- | --- | --- | | Where it renders | its own route, /gw-debug | an overlay, on every page | | Colours | edit theme.ts | colors prop + exported palettes | | Install | copy a folder | npm i + one component | | Customising the UI | fork freely, it is your code | props only |

If you expect to restyle heavily, the folder is still the better choice.


Licence

MIT © 2026 JAINEEL PATEL