Skip to main content

Why Ember Is Defensible

Every vibe-coding tool ships a demo where someone types a prompt and an app appears. The hard part isn't the demo - it's running that platform safely when the prompt comes from an attacker, when the LLM generates something dangerous, when one tenant tries to peek at another's data, when bad packages slip into the build. Ember was designed from day one around those threats. That's the moat.


The five threats that shape everything

A platform that takes untrusted text from anyone, has an LLM convert it to code, then runs that code on infrastructure with secrets nearby - that's a hostile architecture by default. Five threats define the design:

  1. Prompt injection that produces malicious code. A user crafts a prompt so the LLM emits something that steals platform credentials, attacks the infrastructure, or attacks third parties.

  2. Secret exfiltration. Generated code reads platform secrets, customer data from another tenant, or the orchestrator's deploy credentials - and ships them out somewhere.

  3. Cross-tenant escape. One project's container reaches into another project's container, data, or secrets. Either accidentally or intentionally.

  4. Resource abuse. A project mines crypto, sends spam, or launches DDoS attacks from Ember's IP space.

  5. Control-plane compromise. The orchestrator holds the highest-privilege credentials on the platform. Compromising it would compromise every customer.

Every architectural decision in Ember is shaped by these threats. Not as an afterthought. From the first line of code.


The trust model in one diagram

Every component in Ember sits in one of four trust tiers. Information flows in one direction only - from less-trusted to more-trusted layers go through narrow, signed, audited channels. The most-trusted layer never accepts code from the less-trusted ones.

The arrows are one-way trust. Generated code can't talk back to the control plane. User prompts can't reach the control plane as anything other than text data - never as code, never as a system instruction. The control plane is the only thing that holds platform credentials, and nothing else ever sees them.

This is the architectural moat. A competitor can copy the chat UX, copy the templates, even copy the LLM prompting. They can't copy this discipline retrofitted onto an already-running platform - it has to be load-bearing from before the first feature ships.


The seven invariants

Seven rules are enforced in code, not in policy documents. Every red-team test verifies them. If any feature pressures any of them, that feature doesn't ship until the conflict is resolved.

  1. The LLM never sees a real secret. Not in the prompt, not in context, not in examples, never.
  2. Secrets exist at runtime only. Never in source code, never in container images, never in deploy logs, never anywhere a future grep could find them.
  3. Generated code is statically scanned before it ever runs. Every bundle goes through the gate before any cloud resource is created.
  4. Every project gets a unique, never-reused identifier. No name collisions, no leftover state from a previous tenant.
  5. The control plane never executes generated code in its own process. No eval, no dynamic require, no in-process sandbox. Bundles are data; running them is the container's job, not the orchestrator's.
  6. Default-deny outbound traffic per target. A Telegram bot can reach Telegram; that's it. A web app can reach nothing outbound by default.
  7. Every deploy asserts the platform's own authoritative readiness state. Not HTTP probes, not optimistic flags - the platform's source of truth.

When investors ask "what makes you defensible", these seven lines are the answer.


The static security gate

This is the single most consequential defence - the layer that catches what the LLM might emit even when the prompt is hostile. Every generated bundle goes through it before any cloud resource gets allocated. The gate inspects the bundle for seven categories of unsafe patterns:

  • Hardcoded secrets or credentials
  • Code that would execute arbitrary input (eval-style dangers)
  • Filesystem escape attempts
  • Outbound network calls to non-allowlisted hosts
  • Unapproved third-party packages
  • Known malicious patterns (cryptominers, reverse shells, etc.)
  • Reading environment variables that aren't on the per-target allowlist

Each target type (web, Telegram bot, X bot, agent) has its own policy: which packages are allowed, which outbound hosts are reachable, which environment variables can be accessed. Adding a new target type is a single configuration change; the gate's rules adapt automatically.

The gate has been red-team tested against sixteen adversarial prompts across all seven categories. All sixteen were caught. Zero leaked through.

That result is the proof point. Investors asking "but how do you stop hostile prompts" - the answer is: we did. Multiple times. And we re-run the tests before every major release.


Defense in depth

The gate is one layer of seven. Each is independently sufficient against most attack classes. Together they form a wide moat.

A hostile prompt would need to bypass all four red layers to do anything dangerous. The first four (input/auth/spec/generation) are about not generating the bad code; the last four (container/secrets/egress/audit) are about not letting it do damage if it slipped through.

In thirty months of red-team testing across multiple security gates, nothing has gotten past layers 3+4 yet. The combination - an LLM that's been steered with the right system prompt, plus a static gate that catches what the LLM might emit anyway - is the operative defence.


Isolation in practice

Every project gets its own container. That includes:

  • Production deploys - each version is its own container, swapped sequentially on publish. A new version doesn't replace the old one until it's verified healthy. A failed deploy never takes down what's already working.

  • Agent runtimes - each agent project gets a long-lived container with its own provider key (decrypted at boot only), its own webhook secret, its own conversation history. Multiple agent projects can't see each other's anything.

  • Per-tenant databases - when a generated app needs a database, Ember provisions either a per-project Postgres schema (with a role scoped to that schema only) or a "bring your own" Supabase connection. Cross-tenant SELECTs are blocked at the database role level, not just at the application layer. We test this by impersonating one tenant's role and attempting to read another's tables - and confirm it's rejected.

  • Per-project secrets - every container sees only its own secrets. The mechanism that pushes secrets to containers requires the target container name to be in the platform's ledger and owned by the calling orchestrator. A typo or attacker-controlled name is rejected before any secret leaves the control plane.

The one exception (deliberately) is the iteration preview container. There's one per user - not per project. It's a cost optimisation: a user iterating across five projects pays for one preview container, not five. The justification holds because the preview container holds no per-project secrets at all - just the writer authentication key, which is per-user. Switching projects wipes the preview's filesystem; there's nothing in env vars to leak because nothing was ever pushed.

Per-project preview containers (full isolation for the iteration surface too) are on the roadmap.


Auditability

Every privileged action on the platform writes to an append-only audit log. Project creates, deploys, secret writes, agent tool invocations, sweep events, token rotations, destructive operations - every one of them logs the actor, the action, the affected project, and the relevant details (with secret patterns scrubbed before insert).

The audit log is queryable by SQL. An admin endpoint surfaces the last twenty rows for operator review plus the current compute/database diff for orphan detection.

This is the layer that makes Ember credible to enterprise customers. "Prove you can tell me what your platform did with my data last Thursday" is a real question. The audit log answers it.


Security gates passed

Ember's build was structured around explicit phase gates. Each gate has a red-team test suite. Gates are non-negotiable - failures block the next phase from starting.

  • Phase 0 security gate - sixteen adversarial prompts across seven attack classes. 16/16 caught, 0 leaked. PASSED.
  • Phase 2 security gate - twelve multi-target adversarial cases verifying that adding new target types didn't regress the prior gate's protections. All caught. PASSED.
  • Phase 4 security audit - pre-public-beta hardening pass, planned. Will cover auth coverage, RLS audit, rate limiting, dependency CVE audit, secret rotation runbooks, GDPR data-handling.

The audit reports are real documents, not marketing material. They list each attack, each outcome, and the exact defence layer that caught it. Investors can read them.


The honest framing for investors

The thing that's hard to copy isn't the chat UX. It isn't the templates. It isn't even the agent integrations - competitors will ship those too.

The hard part is shipping a platform that runs untrusted code with secrets nearby and doesn't blow up. That requires a discipline that's load-bearing from day one. Trying to retrofit a security model onto a vibe-coding tool that's already growing fast is, in practice, impossible. The decisions you make to ship features fast in year one make the security model expensive to add in year two.

Ember was built the other way around. The security spine existed before the chat UX. The seven invariants existed before the first user did. The static gate was red-team tested before the templates were even written.

That ordering - security first, features after - is the moat. Everything else is replicable. This isn't.