DEV Community

Cover image for Your AI-built app wants your API key. I built drobek so it never gets it.
Tomas Grasl
Tomas Grasl

Posted on

Your AI-built app wants your API key. I built drobek so it never gets it.

The UI is working. Then comes the request: connect an API, save a file, let colleagues sign in. A small vibe-coded app suddenly needs decisions about credentials and access.

I built drobek with a specific boundary: the agent writes the browser app, while backend modules run on the drobek server. A proxy module can attach an API key without putting that key in the generated JavaScript. The agent can configure the connection without receiving the secret value.

An app can start as a useful page and gain backend capabilities as it needs them. I still want to decide what it can reach.

You can see this in a Pokédex, a shared Pixel Wall, and now a game about running the hosting company: Drobek Tycoon.

A Pokédex with no backend of its own

Open the Pokédex and select a Pokémon. The page fetches data from PokéAPI through drobek's proxy module. Its request log makes the calls visible.

The app uses:

import { drobek } from 'drobek';

const response = await drobek.proxy.fetch('pokeapi', '/api/v2/pokemon/25');
if (!response.ok) throw new Error(`PokéAPI request failed: ${response.status}`);
const pokemon = await response.json();
Enter fullscreen mode Exit fullscreen mode

Here, pokeapi names a registered upstream. The server knows its base URL, which methods and paths are allowed, and which apps may call it. The browser sends a request to its own app origin; the proxy checks it and forwards the permitted call.

PokéAPI does not require a key. For an upstream that does, an owner enters the secret in the dashboard and the proxy injects it on the server. MCP tools report whether a secret exists, not its value.

This works for an API-backed dashboard, a weather widget, or a small interface to an existing service. Configure the upstream narrowly enough for that app. A read-only widget usually has no reason to inherit every write endpoint your API exposes.

The proxy also checks destinations against SSRF rules, blocks private and reserved addresses by default, and applies request limits. Those checks matter because “fetch this URL for me” becomes dangerous when the server can reach things the browser cannot. Operators can explicitly allow internal hosts on their own instance; that is an access decision to review.

Keeping a key on the server protects the key itself. It does not make the data returned by the API private. If you permit public callers to read sensitive results, they can still read them.

The basic modules, with jobs for each

These are the built-in pieces an app can use when the operator enables them:

Module What it does A small app that needs it
auth App-user sign-in, allowlists, roles and sessions A tool only colleagues should use
data Stored records with rules for each operation Saved estimates, requests, or shared task lists
files User uploads, access rules, type checks and quotas Avatars or attachments
forms Stores submissions and emails the app's owners A contact form or feedback box
email Notifications to the app's owners Reporting an item for moderation
proxy Controlled requests to registered APIs, with secrets added server-side The Pokédex, or an API-backed internal dashboard

The agent can inspect a module through skill_info('<name>'): its SDK types, configuration, limits and examples. It configures an installed module with configure_module. Changes that broaden access can require confirmation in the dashboard.

The app calls the SDK. Access rules are enforced on the server. Hiding a button in React would not protect the corresponding operation.

The modules behind Pixel Wall

Pixel Wall is a shared 64 × 40 canvas. Visitors can look; signed-in people can paint. Each stroke becomes a database record.

Its module-status panel explains the rest of the app: auth handles email-code sign-in, data stores strokes and profiles, and files handles painter avatars. The message form uses forms; reporting a stroke uses email to notify admins.

The visit and stroke counters come from counter, a separate example module. It is not one of the six built-ins. It sits alongside the standard modules and has its own SDK methods.

You can inspect the wall without signing in. Uploading an avatar or painting requires the relevant user action; a module appearing in the panel does not mean every operation has already run.

A game about running drobek

Drobek Tycoon puts you in charge of a small hosting company. You build servers, hire a team, attract customers and handle incidents across three levels. Salaries, power and rent come out of the company budget.

The game keeps your progress in the browser without requiring sign-in. It also has a shared leaderboard for the best company value at each level, and its gallery entry lists the data module. Local game progress and a shared leaderboard serve different purposes; using a backend for one doesn't mean moving the whole game onto the server.

That gives the module model another use beyond forms and internal tools: a browser game with a shared result to compare after a run.

Your company's SSO can fit here too

Suppose your company has its own identity provider and expects internal apps to use it. On your own drobek instance, you can install a module that contributes a sign-in provider through the auth.provider extension point.

The contract has defineAuthProvider, with begin and callback methods. The provider implements and validates the identity-provider exchange. It returns a verified identity; drobek's auth module retains the allowlist, roles, sessions and audit trail. The provider's client secret stays on the server.

That gives you a place to implement company-specific OAuth/OIDC sign-in. It is an extension contract, not a claim that an adapter for every corporate provider ships ready to use. Also, this extension concerns people signing into the hosted apps. Dashboard administrator login is a separate concern.

For a different requirement, such as an ERP operation with custom validation, write a platform module with its own routes and SDK slice. The scaffold starts with:

npm create drobek-module@latest erp
Enter fullscreen mode Exit fullscreen mode

A module can carry configuration, migrations, secrets and documentation for the agent. The operator installs it and selects the modules the instance runs with DROBEK_MODULES. You can also replace a built-in module with your own package implementing the appropriate contract.

The hosted drobek.app service uses the same public core image available to self-hosters. I keep its deployment and service-specific pieces around that core in my separate operations repository, drobek-web. The public module documentation explains the extension mechanisms you can use for your own instance.

Where I put the security boundary

I don't want every small generated app shipping its own server code into the hosting process. In drobek, the server compiles and serves app files; the app's JavaScript runs in the browser. Backend execution belongs to modules chosen by the operator. Apps also have separate origins from the dashboard.

We can maintain and patch these backend modules centrally. When a module needs a fix, its operator updates that module or the core version containing it. Self-hosters control their own update schedule.

Custom modules are trusted server code. They run in the server process with database access, not in an isolated plugin sandbox. Install and review them accordingly. The architecture reduces what an app author can put on the server; it does not turn arbitrary module code into safe code.

For a new app, choose the smallest set of capabilities that does the job. Try the Pokédex to see a proxy request, or the Pixel Wall to see several modules working together. If your backend requirement is more specific, the open-source core and module contract give you somewhere to implement it.

Top comments (1)

Collapse
 
suppdevbot profile image
DEV SUPPORTS •
You need to verify your account.
Enter fullscreen mode Exit fullscreen mode

tr.ee/dev-to