Skip to content

Canvas reference

pak edited this page Aug 30, 2026 · 4 revisions

Canvas reference

The page to keep open while you work: every key, what the panels do, how selection behaves, which handle starts which kind of link, and what undo actually covers.

The application ships a shortcut sheet under the ? key, rendered from the table in frontend/src/shortcuts.ts. That file only describes: the listeners live in the components that watch the keyboard, and nothing derives them from the table. The sheet and the table can never disagree with each other, but neither is tied to the handlers. This page carries more than the sheet: the keys the sheet leaves out (? itself, the canvas search bar, the dialog Esc), the scope of each key, the cases where a key deliberately does nothing, and the reasoning behind the layout.

What STIX objects and relationships mean is not here. That lives in the in-app guide: https://app.drawmeastix.io/guide

Keyboard shortcuts

The listeners test ctrlKey || metaKey, so on macOS Cmd works everywhere Ctrl is written below. Only the display changes.

Everywhere on the canvas

Keys What it does
Ctrl K Open or close the command palette
Ctrl Z Undo the last deletion
Ctrl B Fold or unfold the left panel

Ctrl+K toggles: pressing it again on an open palette closes it.

Ctrl+B folds whichever left panel is showing. If Frameworks or Scenarios was open, the first press folds it and the second opens Objects, because the unfold target is always Objects.

Canvas, when the caret is not in a text field

Keys What it does
/ Open the canvas search bar
? Open or close the shortcut sheet
l Link focus: selecting an object rings what it is connected to
t Show or hide the labels written on the cards
Esc Show everything again, when a lens is dimming the canvas
Del Delete the selection
Ctrl click or Shift click Add an object to the selection
drag Start a relationship from a grip on any side of an object onto another object
Ctrl V Paste a screenshot from the clipboard as a capture pinned on the canvas

Del only. Backspace is not bound (deleteKeyCode={['Delete']}), which is deliberate on a canvas that sits one tab away from text entry.

Both Ctrl+click and Shift+click are wired for multi-selection because some window managers swallow Ctrl+click before the page ever sees it (this was seen under Hyprland). Shift+click always gets through.

? is matched on the character produced, not on a physical key code, so it works the same on AZERTY and QWERTY.

Canvas search, once the bar is open

Keys What it does
Enter Jump to the next hit and centre on it
Shift Enter Jump to the previous hit
Esc Close the search and clear it

The search matches an entity's name, its type label, its labels, its aliases, and the content of pinned notes. Matching objects keep their colour, the rest are dimmed. The counter reads 3 / 12, current position over total, because on a dense graph how many are left matters as much as how many there are.

Triage tray

The tray sits at the bottom of the canvas and takes these keys only when it holds focus. Click it once to give it focus.

Keys What it does
j or Next candidate
k or Previous candidate
y or Enter Accept: the object goes on the canvas
n or Del Reject: the object is dropped

The keys are bound to the panel and not to the window on purpose: an n listened for globally would reject a candidate while you were working on the canvas.

The cursor does not move when you accept or reject. The next candidate slides into its place, so you can hold y down to clear a batch. Full walkthrough: Triage-walkthrough.

Command palette, once it is open

Keys What it does
Move through the list (it wraps at both ends)
Enter Run the highlighted line
Esc Step back one stage, then close

In relate mode Esc drops the target stage, then the source, then closes. Picking the wrong source should not force you to start over from a closed palette.

Dialogs

Every dialog closes on Esc and gives focus back to whatever opened it. Two dialogs can be stacked, because the command palette is not one of them: Ctrl+K still fires over an open dialog, and the action you run from it opens a second box on top. One Esc closes only the topmost.

When a shortcut deliberately does nothing

This is the part a cheat sheet cannot carry, and the part that looks like a bug when you do not know it.

Situation Ctrl K Ctrl B Ctrl Z / and ?
Caret in an input, textarea or contenteditable fires fires ignored ignored
A dialog is open fires fires fires unless the caret is in a field ignored

Ctrl+Z stands down inside a field so that it stays the browser's own undo of typing. Undoing a deletion while fixing a typo is not what anyone means.

/ and ? stand down under a dialog because they used to fire anyway: search opened behind an opaque backdrop, and the cheat sheet landed on top of a half-filled form.

Ctrl+K is the exception that is meant to be one. It is the only route back to the index of everything, so it stays reachable wherever the caret is.

The command palette

Ctrl+K opens a single index over the canvas objects, object creation, the frameworks, scenarios, relationships and actions. Before it existed, every capability lived behind its own panel, so you had to know where to look before you could look.

The filter is a subsequence match: crx finds Corax, expc2 finds Exfiltration Over C2 Channel. It indexes names and identifiers only, never prose. When scenario descriptions were indexed, corax surfaced four unrelated scenarios, because in a long text almost any short sequence appears in order.

Groups, in order:

Group Holds Cap
On this canvas Every object on the canvas, jump and select 6
Create One line per SDO then SCO type 6
ATT&CK, F3 (fraud), ATLAS (AI systems), AADAPT (digital assets) One group per framework, named after it, from 2 characters typed. "Impersonate Account Holder" and "Spearphishing" are not the same body of knowledge, and you are entitled to know which one answered 6 each
Scenarios The built-in scenario templates 6
Relationships A single entry, reading Relate two objects… until something is selected 6
Actions The list below 6

Six per group is a readability cap, not a completeness one: type more to narrow.

A framework dataset is several hundred entries and is fetched on the first open of the palette, never at startup. The palette searches all four, so all four are loaded when it opens, and each is fetched once. The palette must open instantly, offline included, even if that means having no techniques yet in that first second. If the fetch fails, the palette works without them and says nothing.

Relating from the keyboard

Pick that entry and the palette turns into a two-step picker: source, then target. Whatever is already selected on the canvas is used as the source, one object or ten, so the common case saves a step.

At the target step, the palette lists only objects that can really be linked, in either direction, plus those reachable through a canonical bridge (single source only). Offering an impossible target helps no one: you pick it, and you get a refusal. When the list comes back empty with no search typed, it says so plainly: no object on this canvas can be linked to that selection, in either direction.

Once source and target are picked, the verb dialog opens, exactly as it does for a mouse drag. The matrix and the bridge fallback live in one place, so keyboard and mouse never validate different things.

Actions

Action Hint shown
Export STIX bundle… export
Export image / PDF / Markdown… share
Paste IOCs… import
Arrange the canvas by structure canvas
Show me: no indicator on it lens
Show me: no relationship at all lens
Show me: no TLP of its own lens
Show me: machine-supplied lens
Show me: the export will complain lens
Show everything again lens
Re-layout the graph by relationship canvas
Toggle the objects panel Ctrl B
Toggle the inspector panel
Undo the last deletion Ctrl Z
Search the canvas /
Keyboard shortcuts ?
STIX guide: what links to what help
Your data: what stays here, what leaves help
Enrichment endpoints… settings
Back to investigations navigate

The two help entries are the ones that open a new tab: the STIX guide on the #/guide route, and https://app.drawmeastix.io/about. Enrichment endpoints… opens a dialog in place, and Back to investigations takes you to the investigation list. Endpoint configuration is covered by Enrichment-sidecar, export by Exporting.

What a name field suggests

The name field of an object offers what it can from two characters typed, from three corpora that never overlap. Nothing is ever filled in for you: a suggestion is picked or ignored.

Object being named What is offered Where it comes from
Intrusion set, threat actor ATT&CK groups, then about 850 more adversary names ATT&CK, its three matrices, plus the MISP threat-actor galaxy
Malware, tool, technique The matching entries from the four corpora ATT&CK, F3, ATLAS, AADAPT
Location The ISO 3166-1 countries, by name or by code the iso-codes standard
Everything else Nothing, it is a plain field

The right-hand column of the list names the source: a MITRE identifier (G0016, T1566, F1001, AML.T0051, ADT3003) for an entry from one of the four corpora, misp for a name MITRE does not carry, the two-letter code for a country. That is not decoration. Picking one writes an external reference naming the knowledge base its number belongs to, mitre-attack or mitre-f3 or mitre-atlas or mitre-aadapt; picking a misp one writes none, because there is no MITRE number to point at and inventing one would be a lie in somebody else's platform. The shape of a number cannot be used to guess: F3 publishes T-numbers of its own, so the framework is recorded next to the number rather than deduced from it.

Why countries are worth a list of their own. A location identifier is computed from its name, so FR, France and French Republic are three different objects that will never merge, here or in the platform that reads the bundle. Type FR, pick France, and the country code and the type are filled in with it.

Why the actor list stops where it does. MITRE and the MISP galaxy disagree about where an actor ends: MITRE folds UNC2452 into APT29, the galaxy keeps it separate. Offering both would hand you two canonical names for one adversary. So every galaxy actor whose name ATT&CK already resolves is dropped when the dataset is built, and ATT&CK wins. Typing Midnight Blizzard offers you APT29, as it should.

The corpora are fetched on demand, never at startup, and a fetch that fails leaves the field working as a plain text input without saying anything.

The frameworks panel

The magnifier on the rail opens one search field over four MITRE knowledge bases, with a row of chips above it for choosing which one is being searched: ATT&CK for the intrusion, F3 for the fraud, ATLAS for attacks on AI systems, AADAPT for digital asset payments. Not four modes: one canvas, and a technique picked from any of them is a plain attack-pattern reached by the same verbs as any other. A case can therefore run from a phishing message to a wire transfer to a bridge in one graph and one bundle.

Framework Holds A number looks like
ATT&CK Enterprise, Mobile and ICS as one corpus, plus its groups, malware and tools T1566.002, G0016, S0002
F3 The fraud matrix, eight tactics F1001, and T-numbers of its own
ATLAS Attacks on AI systems, sixteen tactics AML.T0051
AADAPT Exchanges, wallets, contracts, bridges, eleven tactics ADT3003

F3 is the one that overlaps. It reuses 43 ATT&CK techniques by number and six of its eight tactics are ATT&CK tactics. Since our identifiers are computed from the number alone, a technique reached through F3 and the same one reached through ATT&CK are one object, not two cards saying the same thing. The dataset build takes ATT&CK's spelling back for the shared ones, and a test holds that line. ATLAS and AADAPT borrow nothing: they cross-reference ATT&CK, which is not the same as being it, so ADT1195 stays its own object next to T1195.

What ends up in the bundle. The card names the framework, the export writes an external reference to it, and F3, ATLAS and AADAPT references carry a url as well, because their sites are worth reaching and ATT&CK's does not need one. Absent means ATT&CK, so nothing already on a canvas changed meaning when the other three arrived.

Under the search field, a link to a page per framework: #/attack, #/f3, #/atlas, #/aadapt. They are for reading a matrix rather than searching it, which is what you want the first time you meet one.

Scenario forms

A scenario slot holds as many values as your case has. The + next to a field label adds a line, Enter in a line does the same, and each line becomes its own object: three tools give three uses, two C2 addresses give two communicates-with. Typing the same name twice in one slot creates it once, since both would carry the same identifier.

The line under the form is always there, in one of three states. It tells you what will come out unlinked, and which empty slot would connect it if you filled it in. It also tells you when a relationship was not drawn: when both ends of one hold several values, two domains and three addresses do not say which resolves to which, and the six links of every combination would be five false statements and one true one. Draw that pairing yourself on the canvas.

Panels and folding

Element Width How it opens and closes
Rail 44 px Always visible
Left panel: Objects, Frameworks, Scenarios, Labels 180 px One at a time. Click a rail icon to switch, click the active icon to fold. Ctrl+B folds and unfolds
Right column: Inspector and Narrative tabs 300 px The topbar panel icon, the chevron in the tab bar, or the palette action
Minimap 200 px Rendered only while the window is at least 1200 px wide
Triage tray bottom strip Rail tray button, or the tray header. Hidden entirely when there are no candidates

The rail's tray button carries the candidate count as a badge and is disabled at zero, so the curation step announces itself instead of existing only once it is full.

Why the thresholds are what they are

The canvas is the product, and it had ended up at 39 % of the screen on a 1280 px laptop. The thresholds are not arbitrary steps, they follow from one rule: chrome must not exceed 40 % of the window. The constants derive from it, so they track any width change.

Constant Value Meaning
RIGHT_BREAKPOINT 1310 px Below this, the right column starts collapsed
LEFT_BREAKPOINT 560 px Below this, the left panel starts collapsed too
MINIMAP_BREAKPOINT 1200 px Below this, no minimap

One consequence worth knowing, because it looks like the left rule is broken: 560 px is also the width at and under which the application shows its small-screen card instead of the canvas. So unless you took the "Open it anyway" door described below, the left panel starts open on every window you can reach the canvas from, and only the right column folds by itself. It folds on a 1280 px laptop. Behind the opt-in the left rule does apply: under 560 px the left panel starts collapsed.

The minimap is dropped below 1200 px for the reason it exists: at 200 px wide, on an already narrow canvas, it eats the very space it is meant to help you travel.

Your choice wins

Whatever you fold or unfold is written to localStorage under dmas.panels and beats the width rule from then on, permanently. Without that memory the interface would undo on every reload what you had just set, which is the worst failing an adaptive layout can have: feeling like it is fighting you.

If storage is unavailable (private browsing, quota), the width decides and your choice holds for the session only.

One exception the layout makes on its own: selecting anything reopens the right column and switches it to the Inspector tab. That is what makes the automatic folding bearable. On a narrow screen you navigate with the whole canvas, and the panel comes back exactly when you ask to see a record. It switches tab too, because selecting an object is asking to see that object, not to re-read the narrative.

Selection

Gesture Result
Click an object It becomes the single selection, the Inspector shows its record
Ctrl+click or Shift+click another Both are selected
Click a relationship The Inspector shows the verb, the description and the activity window, with a delete button
Click a dashed annotation link The Inspector shows an Unlink panel
Click a pinned note card The Inspector shows the note in full

With more than one entity selected, the Inspector is replaced by the bulk editor, which applies one patch to the whole batch. Non-entity nodes (pinned notes, captures) are filtered out of that batch: only entities go into it.

Multi-selection is also the input to Relate the N selected objects to… in the palette, and to Del.

A caveat that bites in a second tab: if another tab writes to the same data, a banner appears saying what you see may be out of date. A bulk edit made after that would write back properties read before the change. The banner offers a Reload. Nothing merges automatically, and that is a decision, not an omission: the product is single-user.

Reading the canvas

Three codes carry meaning on the canvas, and only one of them is named in the sidebar.

The colour of a card is its STIX type, and the Objects panel lists every type beside its colour, so that one needs no legend. Jewel tones are SDO, muted tones are SCO.

The colour of a relationship is not its verb. STIX has twenty-six verbs available here, and six more hues on top of the eighteen the objects already carry would say "these differ" far louder than they differ. What an analyst reads off a graph is coarser than the verb anyway, so the colour answers one of five questions and the verb stays written on the line for the detail.

Family The question Verbs
Attribution Who is behind it attributed-to, impersonates, owns, authored-by
Capability What it wields, and is made of uses, drops, delivers, downloads, exploits, variant-of, consists-of, has
Victimology Who and what it hits targets, compromises, exfiltrates-to
Detection How it would be seen indicates, based-on
Infrastructure Where it sits, what it talks to communicates-with, beacons-to, resolves-to, belongs-to, located-at, originates-from, hosts, controls
Unspecified Only that they are related related-to, and any verb this list does not know

A verb an import brings in that is not in the table reads as unclassified rather than being guessed into a family.

The diamond at a card's bottom right says there is something to read. It is drawn only when the object carries a note, in the notes' own hue, and in the opinion hue when one of them is an opinion, which outranks a note. What carries the information is the presence of the mark and not its colour: a mark on every card would make you compare hues, a mark on four cards out of thirty is seen without looking. A card with nothing to say draws nothing, and offers its grip when you come near it.

The legend, bottom right beside the minimap, carries the relationship families and the two annotation marks. It folds with the cross and stays folded, remembered under dmas.legend-open; nobody learns a colour code from a panel they have to go and find, and nobody wants it there a second week.

The narrative, and the chronology

The right column's second tab reads the graph back as prose, live, and it is generated from the relationships rather than written: same graph, same words, and no language model anywhere near it.

What it holds depends on what you dated. With no dates it is one list, in the reading order of the attack chain, and it says nothing about chronology because there is nothing to say. As soon as a relationship carries a start time, the panel grows a Chronology: every dated statement, from every subject, in the order it happened, each line saying who did what and when. What carries no date follows it, under Undated, in the order it always had.

The hour, when you set one. A relationship carries a date and, beside it, an optional time. STIX has no notion of a day: start_time is a timestamp with seconds and a Z, so a date-only window goes out as midnight UTC. Leaving the time empty therefore has to keep meaning "that day" rather than asserting midnight, which is why it is a second field and not one control. The chronology orders and groups to the minute, and midnight exactly reads back as a day.

A report gets one thing this panel does not: the same chronology again, one timeline per subject. A 300px column read while working wants the case, not the case and its index. See Exporting for that, and for drawing the chronology rather than listing it.

Lenses: asking the canvas a question

The Show menu holds five questions. A lens moves nothing: it lights the objects that answer the question and steps everything else back, on whatever layout is on screen. Esc puts it away, and so does choosing the same lens again.

Lens Lights
No indicator on it Objects that are not the target of an indicates. Indicators themselves are never lit: an indicator is not something an indicator covers
No relationship at all Objects nothing touches, in either direction
No TLP of its own Objects that will inherit the marking chosen at export time
Machine-supplied What came from an import, a document or an enricher. Pasting IOCs counts as hand-made: the question is whether a human looked at it
The export will complain What the validator has something to say about, the same set the status bar counts

A relationship survives a lens only if both its ends did, or the answer would sit inside a web of bright lines going nowhere the question asked about. A note or a capture answers none of these questions, so it takes the verdict of the object it hangs off.

One lens at a time, and the labels in the Labels panel share the same state: a label and a question cannot both be on, because two answers would fight over the same dimming.

The lenses replaced seven arrangements that laid the objects out in blocks, one per answer. Those were built when the graph could not be drawn at all and made a virtue of ignoring the relationships; once Arrange could draw them, ignoring them stopped being a precaution and became the cost, since the answer to any of these questions is a set of objects and piling that set into a block takes away the context that made those objects mean anything.

The Labels panel

The fourth rail icon lists the labels in use across the investigation, most used first, with how many objects carry each. Clicking one lights those objects, exactly like a lens.

Labels are compared exactly and never case-folded. They are free text and they drift, so ransomware and Ransomware are two labels and a bundle exports them as two; folding them here would hide the drift at the moment a list makes it visible. This panel is the only place in the application where you see your own vocabulary at once, which is half of what it is for.

t shows or hides the labels written on the cards, remembered under dmas.card-labels. Three chips is a third line of text on every object, and on a crowded canvas the same handful of labels comes round again and again, saying little for the ink it costs. The panel keeps listing them either way.

The grips on a node, and what each one starts

An entity card offers a grip on each of its four sides, and one more at its bottom right corner. The four build STIX, the fifth builds annotation, and nothing crosses over.

Grip Position What a drag from it starts Ends up in the bundle
Link Middle of each side, four of them A STIX relationship. The verb dialog opens yes
Annotation Bottom right corner An annotation link, and only to a capture no

Each link grip is a source and a target laid one over the other, so a relationship can start from any side and finish on any side. They are hidden until you bring the pointer near the card, or select it: a mark drawn there permanently reads as "the line arrives here", and that stopped being true when the edges started anchoring themselves. While a link is being dragged, every card that could legally receive it shows its own grips, wherever the pointer happens to be.

There used to be two, a triangle at the top that received and a triangle at the bottom that started, and the graph was drawn along that axis. A relationship now meets a card on whichever side faces the other object, and fans out around the middle of that side when several of them want it, so an edge pointing upwards on the canvas no longer tells you anything. The arrowhead is what says which way it runs, and it always was.

The annotation grip is a pair of overlapping handles, one in each direction, so a capture can be linked from either end. onConnect drops any STIX relationship that would start from there, which is why you cannot accidentally create a relationship by aiming at the wrong corner. It is also the only grip that stays drawn at rest, and what it says then is not about links at all: see Reading the canvas.

Capture nodes (a screenshot pasted with Ctrl+V) carry their pair of annotation handles on their left edge. Drag from there onto an entity, or from an entity's annotation grip onto the capture. Double-click a capture to open it full screen.

Pinned note cards carry a handle on the left too, but it is not connectable. A note's dashed link follows the entity the note belongs to; it is not something you draw, and it cannot be deleted on its own.

The two kinds of edge are drawn so they cannot be confused: a STIX relationship is a solid line, coloured by what it says, with an arrowhead and a verb label; an annotation link is dashed, unarrowed, grey, and labelled annotation at most. Annotation links never reach the bundle, see Exporting.

Which way the relationship goes

The direction of your drag is not the direction of the relationship. The matrix decides.

  1. It tries your direction: are there verbs valid from the source type to the target type?
  2. If not, it tries the reverse.
  3. If neither works and you dragged a single object, it looks for a canonical bridge and offers the intermediate object instead of failing.
  4. If nothing holds, it says so: no valid STIX relationship between these two types, in either direction.

This exists so you can think "link these IPs to this infrastructure" without knowing that an observable is never the source of a relationship towards an SDO, and that the correct reading is infrastructure consists-of ip.

For a batch, only the verbs valid for every type in the selection are offered. A mixed batch with no verb in common will tell you so rather than create half the links.

Deleting

Del on the selection, the small button at a node's top right, or the delete button in the Inspector: all three take exactly the same path, so cleanup and undo behave identically whichever you use.

That top-right button is understated at rest and shows itself when the node is hovered, when it is selected, and when it takes keyboard focus. The last case is not cosmetic: at opacity 0 it was still in the tab order, so keyboard navigation could delete an entity with nothing on screen announcing it.

What each one removes:

Node kind What Del does
Entity Deletes the entity, its relationships and its notes
Capture Deletes the capture
Pinned note Unpins it from the canvas. The note itself stays in the notes panel; deleting it for real is done from there

The note card's button is a minus and not a cross for that reason: it takes the card off the canvas, it does not destroy the note.

Undo

Ctrl+Z undoes deletions, and nothing else. That is a decision: a typo in a name gets retyped, but three quarters of an hour of linking does not come back on its own. Property edits, renames and creations are not on the stack.

Property Value
Depth 25 entries, oldest dropped
Coalescing window 400 ms
Restores Entities (with their properties), relationships, deleted notes
Fallback With an empty stack, Ctrl+Z restores the positions from before the first Arrange
Empty Says "Nothing to undo."

The coalescing window is the detail that makes it usable. Deleting a node makes its edges vanish, and the canvas library reports the edges and the node as two separate events. Without coalescing, one gesture would need two Ctrl+Z, and the first would leave a node stripped of every relationship, which is worse than no undo at all. Everything pushed within 400 ms of the previous entry is undone as one block.

Order matters on restore: entities come back first, then relationships, then notes. A relationship whose endpoint is gone cannot be restored, and you get told how many were dropped rather than a silent partial result.

The entry is only popped once the restores have gone through, so a failure leaves you able to retry. Restores write back on the original identifier, so pressing Ctrl+Z twice during a slow restore breaks nothing.

My layout appears next to Arrange once there is a layout to go back to, and puts the objects where you had them however many arrangements you have tried since: the copy is taken once, before the first one, and not before each. Ctrl+Z falls back to it when the deletion stack is empty, because a Ctrl+Z that does nothing while that button sits on screen is not defensible.

Small screens

At 560 px wide and under, or on a coarse pointer at 820 px and under, the application shows a presentation card rather than the canvas. There is an "Open it anyway" button, and taking it is remembered under dmas.smallscreen, so a tablet with a keyboard is not asked twice.

The canvas genuinely needs a mouse and a keyboard: relating is a drag, and multi-selection is a modifier click.

See also

Clone this wiki locally