Canvases and running services
A canvas is a drawing of the few services you are working on right now. What is on it is what runs.
Canvases
A canvas is drawn, not discovered: which three services out of forty matter today is yours to say. There can be many, each for a different piece of work.
- Choose one from the picker in the header. Canvases are grouped by project.
- Make one with + beside the picker (+ New canvas when there are none yet).
- Rename or delete one with the ⓘ button, which opens Canvas details. Deleting a canvas removes the drawing only; the services themselves are not touched. A canvas cannot be deleted while its services run.
A canvas belongs to the project that was selected when it was made, and is saved with it under
~/.debugt. It can still hold services from several projects: drag from one project, select
another in the left pane, and drag from that one too.
Putting services on a canvas
Drag a service from Services onto the canvas, or double-click it. The first time, Debugt asks What kind of service is this? — see APIs, event services and consumers.
Each node shows:
- A state dot — stopped, starting, running or paused (on a breakpoint).
- Its branch, above the node.
- Its host, and a count of its overrides, below it.
The captions can be hidden from the canvas’s toolbar or its right-click menu (Hide branch, host and overrides).
A node whose project is not open in any VS Code window is drawn unresolved — dotted, in amber — and keeps its place and its lines until the project is opened again.
Running and stopping
The buttons in the canvas’s top-right corner act on the whole canvas:
| Button | Does |
|---|---|
| ▶ Run all | Starts every service on the canvas under the VS Code debugger, one after another. Muted ones are left out. |
| ■ Stop all | Ends every debug session the canvas started. |
| ↻ Restart all | Stops the running services, then starts them again. |
Right-click a node for the same three on one service — Run, Stop, Restart — for the restart you want without disturbing the sessions around it. Select several nodes (Ctrl/Cmd-click, or Shift-drag a box) and right-click to act on the selection.
Things to know:
- One canvas runs at a time. While one is running, another’s Run is disabled until you stop the first or switch back to it.
- An API service needs a host before it can run. Run all tells you which ones are missing it.
- One failing does not stop the rest. The failures are named in a message over the canvas.
- A service started with F5 shows as running on the canvas and can be stopped from there.
- Starting means the debugger has attached; running means the service is really up. See When a service is “running”.
The node menu
Right-click a node:
| Item | Key | Does |
|---|---|---|
| Run, Stop, Restart | R, S, ⇧R | Act on this one service. |
| Mute / Unmute | M | A muted service stays on the drawing but is left out of Run all, and any redirect on its lines is held off. |
| Details | ↵ | Opens Service details. Also opened by double-clicking the node. |
| Overrides | Opens the service’s Overrides — below. | |
| Add request | N | Creates a saved request for this service and opens it in the runner. |
| Show Requests | Finds this service’s requests in the left pane, or says it has none yet. | |
| Open in VS Code | O | Shows the service’s file in the VS Code window that has it open — below. |
| Remove from canvas | Delete | Takes it off this canvas. The service and its requests are untouched. |
| Point this node at… | Shown when its launch configuration is gone: pick what it is called now. |
The menu lists only what applies: no Run on a running service, no Stop on a stopped one. A service paused on a breakpoint counts as running. An item that applies but cannot be used now — an API with no host, a muted service, another canvas running — is greyed, and says why when you hover it. A node whose repo is not open in any VS Code window shows one note in place of the items it cannot do.
Open in VS Code opens the file that is the service, trying in turn: an event service’s
handler, at the function it calls; the launch configuration’s program; a Python module of
the service’s own, or the app it serves (flask --app, uvicorn main:app); the service’s
folder, in the explorer; and last, launch.json.
Keys
The key beside a menu item works while the menu is open. With nothing selected and no menu open, R, S and ⇧R are Run all, Stop all and Restart all, and H hides or shows the captions. Any key on the canvas closes a menu that is open.
Read-only while running
While any service on a canvas is running, the canvas is read-only: nodes do not drag, lines cannot be drawn or removed, and details open for reading. A lock shows beside the canvas’s name. The rules went into those processes when they started, so an edit now would look applied and would not be until a restart.
Run, Stop, adding a request, panning and selecting all still work. A service’s overrides can still be changed: they are held until Save and restart.
Edited and switched
Two marks tell you that a running service may no longer be running the code you are looking at.
edited appears under a node once a file in the service’s folder no longer holds what it
did when the service started. Click it to open Edited files, which lists them; each opens
in VS Code. Files git ignores do not count, nor a file saved again unchanged, nor a binary file
or a debugger’s build (__debug_bin…), and an edit put back takes the mark off. A program that
is a folder, such as a Go package, is watched as that folder. This can be switched off in
Settings → General.
A branch change shows in the branch caption as started-on → current when the checkout
has moved to another branch since the service started.
The canvas’s right-click menu then offers:
- Restart all edited — restarts only the services marked edited.
- Restart all switched — restarts only the services whose branch changed.
- Restart all with changed overrides — restarts only the services whose overrides changed since they started.
The rest keep running.
Redirects
A line between two nodes is a redirect: when the source service calls the remote it would normally reach, send that call to the target running on your machine instead.
Draw a line from one node to another and the New redirect dialog asks what it means:
- When source sends to — the method (or ANY) and the address the service calls today,
such as
https://api.example.com. It is matched as a prefix: the rest of the path and the query are carried across. - Redirect to target — the target’s host, with an optional path.
- Redirect active — untick to send calls to the real address while keeping the rule.
Double-click a line to change it. Right-click a line to Edit redirect…, Turn redirect off or Remove line.
Things to know:
- Rules are applied when a service starts. Change one, then restart the source.
- Each redirected call prints a line in the Debug Console. A request going somewhere other than where the code says is never silent.
- One call can only go to one place. A second line out of the same service that claims the same address is refused. Where prefixes overlap, the more specific rule wins, and on the same address a rule for one method wins over an ANY one, whichever was drawn first.
localhost,127.0.0.1and::1are treated as the same host.- Redirects are applied to Node, Python, Java and Go services. See Language support and limits.
A line into an event service is a trigger rather than a redirect, and nothing can be drawn into a consumer: see APIs, event services and consumers.
Service details
Double-click a node, or choose Details from its menu.
| Field | Meaning |
|---|---|
| Name | The launch configuration’s name. Changed in launch.json, not here. |
| Kind | API service, Event service or Consumer. Change to pick another. |
| Host | For an API: where its requests are sent, and where a redirect into it points. A localhost port is also handed to the service as PORT. Debugt warns if another service already has that port, or something is listening on it now. |
| It has no port | For an API that listens on nothing. |
| Ready pattern | A regular expression matched against the service’s output. Until a line matches, the service shows as starting. |
| Handler, Triggers | For an event service. |
| Protocol | For a consumer: what it takes its messages off. |
| Overrides | How many it has — 2 functions, 1 URL — and Edit overrides, which opens them. |
While the service runs the dialog is read-only: stop it to change these. Its overrides are the exception — below.
Overrides
An override answers something the service calls with a value you set: a library function, or a URL. Use one to make the payment gateway decline, the clock say it is midnight, or a partner’s API answer 503.
A service’s overrides have a window of their own, with a Functions tab and a URLs tab. Open it from Overrides in the node’s menu, from the overrides count under the node, or from Edit overrides in its details. The count turns red when something is wrong: a library that is not open, an entry that is gone, or overrides changed since the service started.
Each tab has two halves: This service only, the service’s own, and From project libraries, entries it uses from a library. A change is saved as you make it. Each row has a ⋯ menu — also on right-click — to Edit it, Clone it, Disable or Enable it without losing it, or remove it.
One value per call at a time. Only one of a service’s own overrides for the same call — the same verb and URL, or the same module and function — can be on. Keep the others as values to switch between: Clone adds a copy switched off, and ticking a row’s box switches off any other for the same call. A row left on below another for the same call answers nothing, and is marked not answering.
Function overrides
On the Functions tab, choose + Add override. Under When the service calls:
- Module — as the service imports it: a package (
somelib), a file in the service (./src/lib/pay.js), or a dotted Python module (app.clients.pay). - Function —
someFunction,Client.prototype.sendfor a method, ordefaultfor the module’s default export. A module written asmodule.exports = fnis that one function: name itdefault.
Under Answer with:
- Replace — never calls the real function; every call returns your value.
- Merge — calls the real function and deep-merges your value into what it returns.
- Value (JSON).
Overrides apply on the next start, to every call the service’s own code makes. A call the library makes to its own function from inside itself is not overridden. They work in Node and Python services.
Things to know:
- A relative path is read from the service’s folder — its launch configuration’s
cwd— not from the file that requires it. Ifsrc/jobs/fetch.jsrequires./parse-rows, the override names./src/jobs/parse-rows. If no file is there, the Debug Console says so as the service starts, and suggests the path you may have meant. Nothing is overridden.
URL overrides
A URL override answers a call the service makes — by verb and URL — with a status, headers and body you set. The call never leaves your machine.
On the URLs tab, choose + Add URL override and give the Verb (one, or Every verb), the URL the service calls, the Status, and a Response body (None, JSON, Text or XML). Every call to that address, and to anything under it, is answered there.
- Where URLs overlap, the longest one wins, whatever their order: an override for
https://api.example.comand one forhttps://api.example.com/v1/orderscan both be on, and calls to/v1/ordersget the second. On the same URL, an override for one verb wins over an Every verb one, whatever their order. - Only one override for the same verb and URL is on at a time — see One value per call.
- An answered call still shows in the trace, as the call the service made, marked override.
While the service runs
Overrides are read when a service starts, but they can be changed while it runs. Changes are held, not saved, until you choose Save and restart, which saves them and restarts the service with them. Closing the window first asks whether to discard them.
When a library entry a running service uses is changed, that service is running with overrides that have changed since it started. The canvas’s right-click menu then offers Restart all with changed overrides.
Override libraries
An override worth keeping can be kept in a library: named overrides that belong to a
project, for any service on any canvas to use. Open them from Override Libraries at the top
of the page. Each project has its own, saved under ~/.debugt in
projects/<project>/overrides.json.
- Using one. In a service’s Overrides, choose + Attach from a library. Entries from every open project’s library can be attached, and they are listed under From project libraries.
- Linked, not copied. An attached entry stays the library’s. Edit in library changes it for every service that uses it; if that is more than one, Debugt asks Change it everywhere?, with Save for all or Save as new entry. Make a copy gives the service a copy of its own, which library edits no longer change.
- Moving one in. Move to library on one of the service’s own overrides moves it into a library, by name.
- Several values for one call. A library can hold as many entries for the same call as you like — Login ok, Login expired, Login 500 — for each service to attach the one it needs. Clone on an entry adds a copy under a name of its own: Login ok copy.
- Off for one service. Disable on an attached entry switches it off for that service only; the library is not changed.
- Removing an attached entry takes it off the service. It stays in the library.
Things to know:
- The service’s own override wins over an attached one that answers the same call, and an entry attached earlier wins over one attached later.
- A library names packages, not paths. A function override in a library must name a
package, a builtin (
node:fs) or a dotted Python module. One that names a file by path, relative or in full, is refused there: a relative path would be read from each service’s own folder, and a full one is this machine’s alone. It stays on the service — Move to library is not offered for it, and + Attach from a library leaves such entries out. An older library entry that names a path is kept, marked Not usable: a path, and not applied on the services that use it, which say why. - On the Free plan, a library holds up to 5 overrides in each project, functions and URLs together; Pro has no limit. Override Libraries shows how full each one is — 3 of 5. The limit is checked only as an override is added to a library — by + Add override, + Add URL override, Move to library or Save as new entry — and a full library says so in place of the way to add one. One already kept is still edited, attached and applied, whatever the plan. A service’s own overrides have no limit.
- A library that cannot be read stops the service starting, rather than run it with calls answered by the real thing. Open the project that holds the library, or remove the entry.
The canvas console
At the bottom-left of the canvas: a running log of services starting, stopping and coming up,
and each send as it comes back. Type in its box to keep only the lines with every word you type —
a service’s name, error, a trace id — with the words marked; Escape clears it. Drag its top edge, its right edge, or
its top-right corner to resize it both ways at once; double-click an edge to put it back. Its
font size and background are adjustable from its own controls.
Several VS Code windows
Each VS Code window can only drive the debugger for the folders it has open. So the windows connect to each other: the first one opened serves Debugt’s page, and the others register their projects with it. The header shows how many windows are connected.
A service is always started in the window that has its project open, which is where its breakpoints are hit. If the window serving the page closes, another takes over at the same address and the page reconnects on its own.
After updating Debugt, reload any VS Code window left open from before: a window still on the older version is turned away with a message asking for that.