Diagrams that keep their shape.

A small text language with a constraint solver. When a preference is dropped, the editor marks the statement's source span with a warning.

use icons aws

Users: icon aws:users "Users"
ChatClient: icon aws:client "Chat client"

MainRegion: [
  ApiGateway: icon aws:amazon-api-gateway "Amazon\nAPI Gateway"
  Lambda: icon aws:aws-lambda "AWS\nLambda"
  Rekognition: icon aws:amazon-rekognition "Amazon\nRekognition"
] "AWS Region"

Users -> ChatClient "(1)"
ChatClient -> ApiGateway "(2)"
ApiGateway -> Lambda "(3)"
Lambda <-> Rekognition "(4)"
Lambda -> ChatClient "(5)"

One language, every shape of diagram

Combine icons from ten packs, styled fills, nested groups, and routed connectors while a constraint solver lays it all out for you.

// The load balancer only talks to the app server; the app server owns both stores.
// Ideal rendering: tiers side by side, LB above App, and App's two outbound
// edges reaching the data tier as separate, non-crossing runs.

column Web: LB App
column Data: PG Cache

Web: [
  LB:  box "Load Balancer" fill #dbeafe stroke #2563eb
  App: box "App Server" fill #dbeafe stroke #2563eb
] "Web Tier" fill #2563eb bold

Data: [
  PG:    box "Postgres" fill #dcfce7 stroke #16a34a
  Cache: box "Redis" fill #fef9c3 stroke #ca8a04
] "Data Tier" fill #16a34a bold

LB -> App
App -> PG "SQL"
App -> Cache "cache"
A two-tier architecture diagram: a Web Tier group where a load balancer feeds an app server, and a Data Tier group with Postgres and Redis; the app server reaches Postgres over a labelled SQL edge and Redis over a labelled cache edge
Style fills and strokes, nest related nodes into labelled groups.
Open this source in the editor →
direction down
require column MainSpine: Start Authn Authz Ok
require row AuthenticationReject: Authn Unauth
require row AuthorizationReject: Authz Forbid

// Two gates that fail differently: 401 means "we don't know who you are",
// 403 means "we know, and no". Collapsing them into one check is the usual
// bug in a hand-drawn auth flow.
//
// A column keeps the successful path easy to scan; two rows peel each rejection
// away from the main decision spine.

Start: box "Request"
Authn: box "Authenticated?" fill #fef08a
Authz: box "Authorized?" fill #fef08a

Out: [
  Unauth: box "401 Unauthorized" fill #fee2e2
  Forbid: box "403 Forbidden" fill #fee2e2
  Ok:     box "200 OK" fill #dcfce7
] "Outcomes"

Start -> Authn
Authn -> Unauth "no"
Authn -> Authz "yes"
Authz -> Forbid "no"
Authz -> Ok "yes"
A manually constrained authentication decision flow: request, authentication, authorization, successful response, and distinct unauthorized and forbidden branches
Add only the rows and column that communicate the decision structure; SheepText handles the remaining placement and routing.
Open this source in the editor →

Constraints, not coordinates

Say what is in the diagram and how it connects; the solver places it. Write a constraint only where you mean one; when two of them cannot both hold, SheepText tells you which.