Approved patterns
An approved pattern is a diagram your organisation drew once, the way it wants every team to draw it: the parts that must stay are fixed, and the parts each team names are slots. Agents connected to the organisation start from it.
TL;DR
| Question | Answer |
|---|---|
| What is a pattern? | A diagram with fixed parts and named slots, in one or more flavours (neutral, AWS, Azure, Google Cloud), with a title, a summary and advice for agents. |
| Why have them? | So every team's diagram of the same thing has the same shape and the same guardrails, such as the firewall in front of every API. |
| What is fixed and what is a slot? | Fixed parts stay exactly as drawn. A slot is a label a team fills in, such as a service name, written {{slot-id}}. |
| Where are they made? | The organisation's Patterns tab, with New pattern. The owner and admins make them; every member reads them. |
| Do I have to write source? | No. Start from one of twelve built-in starters, or a diagram already in your library, then mark the labels teams rename. |
| How do agents use them? | find_pattern lists your patterns first, marked Your organisation's pattern. draw_from_pattern fills the slots with the agent's values. |
| Can agents be held to them? | Agents start from approved patterns on the Settings tab makes find_pattern answer with yours only. It guides, it does not block. |
| Can an agent write one? | Yes. A pattern is one JSON file. An agent writes it, checks it with check_pattern, and an owner's or admin's agent saves it with publish_pattern. See Pattern files and agents. |
| Can I import or export one? | Yes. A pattern file on the Start step imports a .json file; every card and the form download one. |
| Which plan? | Enterprise. Up to 50 patterns. |
What a pattern is
A pattern is Plotwork source with two kinds of part.
| Part | What it is | Example |
|---|---|---|
| Fixed part | Drawn exactly as saved, in every diagram made from the pattern | The web application firewall, the audit log, the segmentation firewall |
| Slot | A label each team fills in, written {{slot-id}} inside a quoted label | {{service}}, {{database}}, {{region}} |
Each slot has a default, which is drawn when an agent gives no value, and one line on what belongs there, which the agent reads. Every pattern has the slots title and description, so each diagram gets its own title and alt text.
Fixed parts and slots, worked through
Your security team insists that every public web app sits behind the web application firewall and keeps its secrets in the vault. The team that owns the app names the app and its database. This is the pattern, drawn with its defaults:
flow "Customer web app" direction=down desc="Customers reach the web app through the web application firewall. The app keeps its data in its database and reads its secrets from the vault."
node users client "Customers"node waf gateway "Web application firewall" icon=lucide:shieldnode app service "Web app"node db database "App database"node vault service "Secrets vault" icon=lucide:key-round
users -> wafwaf -> appapp -> dbapp -> vault : "reads secrets" role=identityNodes:
- Customers, client
- Web application firewall, gateway
- Web app, service
- App database, database
- Secrets vault, service
Connections:
- Customers to Web application firewall
- Web application firewall to Web app
- Web app to App database
- Web app to Secrets vault (identity line): reads secrets
Its source marks the three labels a team renames, and leaves the firewall and the vault fixed:
flow "{{title}}" direction=down desc="{{description}}"
node users client "{{users}}"node waf gateway "Web application firewall" icon=lucide:shieldnode app service "{{app}}"node db database "{{database}}"node vault service "Secrets vault" icon=lucide:key-roundThe payments team's agent calls draw_from_pattern with app set to Payments API, database to Ledger and users to Shoppers. The firewall and the vault come out as they were saved:
flow "Payments API" direction=down desc="Shoppers reach the payments API through the web application firewall. The API keeps payments in the ledger and reads its secrets from the vault."
node users client "Shoppers"node waf gateway "Web application firewall" icon=lucide:shieldnode app service "Payments API"node db database "Ledger"node vault service "Secrets vault" icon=lucide:key-round
users -> wafwaf -> appapp -> dbapp -> vault : "reads secrets" role=identityNodes:
- Shoppers, client
- Web application firewall, gateway
- Payments API, service
- Ledger, database
- Secrets vault, service
Connections:
- Shoppers to Web application firewall
- Web application firewall to Payments API
- Payments API to Ledger
- Payments API to Secrets vault (identity line): reads secrets
| Make it a slot when | Keep it fixed when |
|---|---|
| Each team has its own name for it: a service, a database, a region, an address range | Every team must draw it the same: a firewall, a gateway, an approval step, an audit log |
| Leaving it out still draws a sensible diagram with the default | Renaming it would hide a control an auditor looks for |
Flavours and icons
A flavour is the same diagram drawn with one provider's icons. An agent that asks draw_from_pattern for aws gets the AWS flavour; one that leaves the flavour out gets the first one.
| Flavour | Drawn with |
|---|---|
| Neutral | Generic icons, for any provider or none |
| AWS | AWS icons, with provider=aws on the header |
| Azure | Azure icons, with provider=azure |
| Google Cloud | Google Cloud icons, with provider=gcp |
On the Flavours step, turning a provider on copies the first flavour with its header naming the provider. Each box is then listed with the icon it has in the first flavour, and a search of the icon catalog finds that provider's icon for it: a load balancer, a queue, a database. Every flavour uses the same slots, so an agent fills one list whichever it picks.
How agents use them
| Step | What the agent does |
|---|---|
| 1 | Calls find_pattern with the request in plain words. Your patterns come first, marked Your organisation's pattern, with ids such as org:payments-service. |
| 2 | Calls get_pattern to read the slots, what each one means, and your advice on what to keep, rename and split. |
| 3 | Calls draw_from_pattern with the id, a flavour and a value for each slot it fills. A value that would break a label comes back as a problem with a hint, never a broken diagram. |
With Agents start from approved patterns on, find_pattern answers with your patterns only and tells the agent to start from them. When none fits, the agent draws from nothing as before.
Create your first pattern
The New pattern button on the Patterns tab opens six steps. Save is on every step after the first; Try it is optional.
| Step | What you do |
|---|---|
| 1. Start | Choose a built-in starter, a diagram from your library, a pattern file, pasted source, or a blank one with two boxes. |
| 2. Source | Change the diagram in the editor. The preview draws every slot at its default, errors show with their line and hint, and the pattern check says when it is ready to save. Select words in a label and choose Mark as slot to make them a slot. |
| 3. Slots | Each slot is a row with its default and what an agent may put there. The fixed parts are listed under it; the plus beside one makes it a slot, and the bin beside a slot makes it fixed again. |
| 4. Flavours | Turn on the providers your teams use, and choose each box's icon for that provider. |
| 5. Details | The name (it becomes the id org: and the name, and never changes), the title, one sentence on what it shows, when to use it, tags, and your advice to agents. |
| 6. Try it | Fill the slots with sample values, pick a flavour, and draw what an agent would get. |
Pattern files and agents
A pattern file is the whole pattern as JSON, saved as <name>.pattern.json. It is what an agent writes, what check_pattern checks, what publish_pattern saves, what the Patterns tab imports and downloads, and a valid body for POST /orgs/{org}/patterns as it is. Its JSON Schema, with a description of every field, is at docs.plotwork.dev/schemas/plotwork.pattern.json (opens in a new tab); name it in $schema and an editor completes and checks the file.
{ "$schema": "https://docs.plotwork.dev/schemas/plotwork.pattern.json", "name": "customer-web-app", "title": "Customer web app", "summary": "Customers reach the web app through the web application firewall, and the app reads its secrets from the vault.", "whenToUse": "Start here for any public web app. For an internal tool, start from the internal app pattern.", "tags": [ "web app", "waf", "vault" ], "templates": { "neutral": "flow \"{{title}}\" direction=down\n\nnode users client \"{{users}}\"\nnode waf gateway \"Web application firewall\"\nnode app service \"{{app}}\"\nnode vault service \"Secrets vault\"\n\nusers -> waf\nwaf -> app\napp -> vault : \"reads secrets\" role=identity" }, "slots": [ { "id": "title", "default": "Customer web app", "meaning": "The diagram title, which is also its accessible name." }, { "id": "users", "default": "Customers", "meaning": "Who uses the app." }, { "id": "app", "default": "Web app", "meaning": "The service the team runs." } ], "notes": { "keep": "Keep the firewall in front of the app and the vault beside it: security reviews look for both." }}| Field | What it holds |
|---|---|
name | Lowercase words joined with hyphens. The id becomes org:<name>, and it never changes once saved. |
title, summary, whenToUse, tags | The words find_pattern matches and shows. |
templates | Plotwork source for each flavour (neutral, aws, azure, gcp), at least one. Fixed parts are plain text; each part a team fills in is a {{slot}} inside a quoted label. |
slots | Every {{slot}} the templates use: its id, its default (up to 60 characters, 600 for description), and one line on what belongs there. |
notes | What to keep, what to rename, and when to split, each optional. |
How an agent makes one
| Step | Tool | What happens |
|---|---|---|
| 1 | get_pattern with file: true | Gives a built-in or an organisation pattern as a file to start from, fitted to an organisation's limits. Rename it. |
| 2 | check_pattern | Checks the file as a save checks it, with no network. Every problem comes back with its field and a fix; a valid file comes back with each flavour filled with its defaults. |
| 3 | draw_diagram | Draws each source check_pattern returned, so the agent fixes warnings and the person sees the picture. |
| 4 | publish_pattern | Saves the file to the organisation, on the hosted server. A name it has is refused unless replace is true, which the agent sets only after you say so. |
Without the hosted server, or without the right role, the agent saves the file as <name>.pattern.json for an owner or admin to import on the Patterns tab.
Who can publish one
| Caller | publish_pattern |
|---|---|
An owner or admin, with their own key or browser sign-in, naming the organisation (organization) | Saves it |
| An owner's or admin's agent connected to the organisation | Saves it |
| A member, a reader, or someone outside the organisation | Refused, with who can |
| An organisation key from the Keys tab | Refused: it acts for no one, and a pattern steers every agent of the organisation |
| An organisation not on Enterprise | Refused; keep the file to import later |
Every save is in the audit log as orgs.pattern.create or orgs.pattern.update, naming the person or the agent connection. With the REST API, send the file as it is: curl -X POST https://api.plotwork.dev/orgs/<org>/patterns -H 'content-type: application/json' --data @customer-web-app.pattern.json from a signed-in session.
Starters
Each starter is a built-in pattern that find_pattern offers everyone. Choosing one copies it, with every flavour, into your form; the built-in one does not change. All twelve come in neutral, AWS, Azure and Google Cloud.
| Starter | Id | What it draws |
|---|---|---|
| Hub and spoke network | hub-and-spoke-network | The hub firewall every route passes, spokes that never connect directly |
| Zero trust access to private apps | zero-trust-access | The identity provider with MFA, the device posture check, the access proxy, logs to the SIEM |
| Three-tier web app behind a WAF | web-app-behind-waf | The CDN and the web application firewall in front of the load balancer |
| Event-driven pipeline | event-driven-pipeline | The event bus between services, the dead-letter queue |
| Data lake and analytics | data-lake-analytics | Raw and curated zones, the catalog |
| GitOps delivery to clusters | gitops-kubernetes-delivery | Build, test and scan, the promotion pull request, the approval before production |
| Multi-region failover | multi-region-failover | Global routing, the standby region with replicated data |
| Single sign-on and SCIM provisioning | sso-scim-integration | Sign-in through the identity provider, provisioning and deprovisioning, the audit log |
| Monitoring and observability stack | observability-stack | One OpenTelemetry collector, metrics, logs and traces, alerting to on-call |
| Segmented cardholder data environment | pci-cardholder-data-environment | The WAF, the segmentation firewalls, tokenisation, the card vault, key management, file integrity monitoring |
| Internal developer platform | internal-developer-platform | Golden paths, policy checks on infrastructure as code, secrets, cost reporting |
| Retrieval-augmented AI application | ai-rag-application | The AI gateway, input and output guardrails, the audit log of prompts and answers |
A built-in pattern can hold more than an organisation's 12 tags and 20 slots. A copy keeps the first 12 tags, and any slot past the twentieth becomes a fixed part with its default.
Troubleshooting
| Problem | Fix |
|---|---|
| Declare the slots it uses | A {{slot}} in the source has no row. Open Slots and give it a default and a meaning, or remove the marker. |
| No template uses this slot | A slot is listed that no flavour uses. Make it fixed with the bin, or put {{slot-id}} in a label. |
| The default is longer than 60 characters | Shorten it: a default must fit a label. Only description may run to 600. |
| Every flavour draws the same kind of diagram | One flavour is a different kind, such as a sequence. Start that flavour again from the first one on Flavours. |
| Mark as slot does nothing | Select words inside one quoted label. A marker anywhere else could break the source. |
| No preview | The browser could not start the drawing engine. The pattern still saves, and the API checks it the same way. |
| An organisation key from the Keys tab cannot change the organisation's patterns | Connect the agent as yourself (your own key or a browser sign-in), as an owner or admin, and name the organisation. |
| The organisation has a pattern of that name | Change name for a new pattern, or let the agent call publish_pattern again with replace: true to change it. |
| An imported file opens with problems | Its shape was right and a rule was not, such as a slot no template uses. Each step marks the problems to fix; save when none are left. |
| An agent ignores the pattern | Check that it connects through the organisation's connection or one of its keys: a person's own connection does not see the organisation's patterns. |
See also
| Doc | Why |
|---|---|
| Retention, legal hold and approved patterns | The policy that holds agents to your patterns, and what is recorded |
| Architecture patterns | Every built-in pattern, with its slots and flavours |
| Cloud architecture | Provider icons and group types |
| MCP server | find_pattern, get_pattern, draw_from_pattern, check_pattern and publish_pattern |