Search Docs
Loading...
Skip to content

Security

Check what the code your design agent’s model writes can reach, where each of your keys lives, and what data IMG.LY receives. Text the model reads, such as a client’s file, can steer that code.

What the Model Can Reach#

A design agent has these parts:

  • Your product API is the backend your product already has: sign-in, your designs and files, and the links your browser fetches files by.
  • Your agent runner is your code that runs the agent loop, the loop that calls the model, runs its tool calls, and repeats. It runs the design tools the SDK gives it, and reads and writes designs through your design store, the designs option. It may be part of your product API or a process of its own.
  • The executor runs CE.SDK, IMG.LY’s design engine, and the short JavaScript programs the model writes to change a design. It is IMG.LY’s Docker image on your infrastructure. The model’s code runs nowhere else.
  • Your plugin modules are CE.SDK plugins your runner sends to the executor with each engine session. They register your asset sources: where the engine searches and fetches the images, fonts, and colors of a tenant, one customer account of yours. A plugin gets options from your runner, usually a token to call your API for one person’s assets.
  • A conversation is your id for one chat or job, passed to prepareCoDesign({ conversation }). An executor serves one conversation.

The model is steered by text it reads: your user’s brief, text inside a file your user imports, the names of images in a library. Any of that can carry a prompt injection, text written to make the model do something else. So treat every program the model writes as possibly hostile, even though it comes from your own agent.

What the model’s code can reach: in the executor, a server process keeps the license key and executor token out of reach and talks to api.img.ly, while the engine holds only a grant that pays for nothing and this person’s token; the engine can reach your API with this person’s rights, IMG.LY’s CDN, and your CDN and font host, and your egress allow-list blocks every other host.

The model’s code runs in the engine process with that process’s full rights. Nothing in the engine or the SDK limits it. So:

  • The model’s code can make any network request the network allows. An egress allow-list, a list of the only hosts the container may reach, enforced outside the container, stops that (see Allow Only the Hosts the Executor Needs).
  • The model’s code acts as the end user, and can read what your plugins were given. Your plugin modules run in the same process: the model’s code can read their options, call their functions, and see their requests. So give them the token of the person the agent works for, for this session, valid for minutes rather than days (it must outlive only the calls that send it), never a token that reaches other users’ or tenants’ data. Only your own engineers write plugin modules, never your users, because a plugin module runs in the engine process with everything you give it.
  • The model’s code cannot reach your license key. A separate server process in the executor holds the key. The engine runs on a grant, a license IMG.LY issues for that one engine that pays for nothing (see Why Your License Key Is Safe in the Container).
  • The model’s code cannot store a design. The executor returns each changed design to your runner, which saves it through your design store. The SDK first checks that nobody saved a newer version and that no newer CE.SDK saved the design (see Concurrent Edits and Versions and Upgrades). The executor holds no route or token for your designs, so the model’s code cannot skip those checks or write another design. It can still put anything into the design it edits, as the model can.
  • Give your plugins’ token no right to write designs, because the model’s code can read it.
  • The model’s code never gets your model key or your database. It can fetch assets only with the token your plugin was given for one person.
  • The model’s code never shares a process with another conversation. An executor refuses every conversation but its first with the error executor_bound, because an engine that is reset and reused keeps much of what the last one loaded, and the model’s code can leave timers or files behind.

Your editor saves through a route of your product API, and the browser belongs to the person, who can change any code that runs there. So that route must check the user’s rights and refuse a save from an older CE.SDK itself, with the SDK’s assertCanSave (see Your Editor).

Where Keys Live#

Which secret lives where: the runner holds the model provider key, your CoDesign license key for IMG.LY’s AI gateway (optional), the token-signing secret, and the executor token; your product API holds the same signing secret; the executor checks the executor token, receives the short-lived token, which the model’s code can read, and keeps the CoDesign license key out of the model’s reach.

Key What it does Where it lives
Your CoDesign license key Pays for exports from your IMG.LY account The executor image’s server process, out of the model’s reach. If a platform starts the containers for you, also that platform’s secret store.
The engine’s grant Starts the engine. Pays for nothing. The engine, readable by the model’s code. The executor gets it from IMG.LY itself.
The executor token (EXECUTOR_TOKEN) Lets only your runner call the executor image Your runner and its executors: one secret you make.
Your model key (or your CoDesign license key, with IMG.LY’s AI gateway) Calls the model, and IMG.LY’s AI gateway for the agent’s image generation Your runner only. With IMG.LY’s gateway, that is the same license key the executor holds, so it lives in both.
Your token-signing secret Signs the short-lived tokens of one person that your plugin modules carry Your product API, and your runner if it mints those tokens. The executor gets the tokens, never the secret.
Your storage credentials Read and write designs and files Your runner and your product API, never the executor
Tokens in your plugins’ options Let a plugin search and fetch one person’s assets The executor, readable by the model’s code: per user, per session, short-lived

The rule behind the table: never give the executor a secret it does not need. Whatever the executor holds, apart from the license key in the image’s server process, the model’s code can read. So a short-lived credential (the grant, or the tokens in your plugins’ options) may sit where the model’s code can read it; a key, such as your CoDesign license key, never may ( Credentials).

A grant lets anyone who reads it start a CE.SDK engine for at most 24 hours. It pays for no export, so whoever takes it can render, not charge your account.

Whoever has the executor token and reaches an executor’s port can open sessions there; a runner with another token gets the error executor_unauthorized. So keep the executor image’s port reachable only from your runner, and a leaked executor token is not enough on its own.

Why Your License Key Is Safe in the Container#

The image runs two processes. A server holds your license key and the executor token, talks to your runner and to IMG.LY, and does the counting and charging. A second process runs the engine and the model’s code, on a license IMG.LY issues for that engine. That license unlocks CE.SDK, pays for nothing, and does not contain your key.

Both run as the same unprivileged user, so three settings keep the engine out of the server:

  • The Node binary cannot be read, which stops any other process of that user from reading the server’s memory or environment.
  • The server ignores the signal that would open Node’s debugger.
  • The server is the container’s first process, which the kernel protects from being killed or frozen from inside.

When it exits, the server ends every other process before it lets go of its port, so nothing the model’s code left behind can take the port over and receive your runner’s token. That is why the image must start as the container’s first process, as its own user and without a debugger, and why it refuses to start otherwise ( Run the Image Hardened).

What IMG.LY Receives#

Your designs, your files, and your exports stay in your storage and pass only through your runner and your executors. What reaches IMG.LY:

  • When an executor starts: your license key and the CE.SDK version, sent to api.img.ly to activate the license.
  • For each export: the kind of file, the count it is charged for (such as a PDF’s pages), and the render id, sent to Metering (IMG.LY’s billing service) with your license key. The render id is a hash of the design’s id, its content, its version, the format, and the export options. The file and the design itself are not sent. What is charged is in Billing.
  • From the engine: CE.SDK itself, the engine inside the executor, reports each export to IMG.LY, which IMG.LY uses to audit what a license claims.
  • From IMG.LY’s CDN (cdn.img.ly), the engine requests CE.SDK’s assets, fonts from the Google Fonts catalog, and the emoji font.
  • When you use IMG.LY’s AI gateway: the requests sent to it. With image generation, that is each generation’s prompt and settings. With the gateway as your model provider, it is everything your runner sends the model, the rendered previews of the design included.

The SDK, the code your runner calls, sends no usage data of its own to IMG.LY: its tool-call events go only to the listeners you pass in events.

Next Steps#

  • Host the Executor — run the image hardened, with closed egress and a new container for each conversation.
  • Connect Your Storage — build the design store your runner saves through.
  • Versions and Upgrades — make your save routes refuse a save from an older CE.SDK.
  • Deploy — check each item before your first real user.