DOCUMENTATION / v0.1
Use the smallest
maxforge surface that works.
Compile text into .maxpat files, generate large repeated
graphs, or let an HTTP-capable agent inspect and replace an explicitly
managed patch scope. These are separate workflows with separate
installation requirements.
maxforge is not affiliated with, endorsed by, or sponsored by Cycling '74.
Choose a surface
Do not install the native external just to compile a file. Do not start the HTTP server when a deterministic CLI command is enough. The extra layers only make sense when an agent must inspect or mutate an open Max patch.
| Goal | Use | Requires Max running |
|---|---|---|
Compile or validate .maxdsl |
npx maxforge |
No |
| Use maxforge from Node.js code | npm install maxforge |
No |
Generate thispatcher commands |
Library API | Only when applying them |
| Inspect or replace an open patch with an agent | maxforge + its automatic local service |
Yes |
| Give an agent reliable operating instructions | maxforge Skills | No; Skills are instructions |
Installation
Run the CLI without a global install
The package is published to npm. Pin a version in automation; use @latest for interactive evaluation.
npx maxforge@latest --help
Install the Node.js library
npm install maxforge
Install the native Max package
-
01
Choose the matching
maxforge-vX.Y.Z.zip. -
02
Extract the top-level
maxforgedirectory into the Max packages directory. -
03
Restart Max, then create a
maxforgeobject or open its help patch.
~/Documents/Max 9/Packages/%USERPROFILE%\Documents\Max 9\Packages\Versioned release archives are Developer ID signed and notarized by CI. Local builds remain ad-hoc signed and are not equivalent release artifacts.
Compile a first patch
Create basic.maxdsl:
patch "Basic Synth"
freq = number
mt = mtof
osc = cycle~ 440
mul = *~ 0.5
vol = gain~
dac = ezdac~
freq -> mt -> osc -> mul -> vol -> dac
vol[1] -> dac[1]
Validate, then compile:
npx maxforge@latest validate basic.maxdsl
npx maxforge@latest compile basic.maxdsl -o basic.maxpat
Open basic.maxpat in Max. The generated JSON is ordinary Max patcher data; maxforge is not required to keep the compiled file open.
CLI reference
| Command | Purpose |
|---|---|
compile input.maxdsl -o output.maxpat | Compile DSL to a Max patch. |
compile input.maxdsl --clipboard | Emit compressed patch text pasteable into Max. |
validate input.maxdsl | Parse and validate without writing output. |
decompile patch.maxpat -o patch.maxdsl | Recover structural DSL and explicit positions. |
from-clipboard -o patch.maxdsl | Read compressed patch text from stdin and emit DSL. |
plan desired.maxdsl --scope voices -o plan.json | Create an ordered managed PatchPlan. |
doctor --input input.maxdsl | Validate project catalogs and derive abstraction ports. |
catalog cycle~ --json | Search effective project and built-in object metadata. |
bundle input.maxdsl -o package | Build a Max package directory with declared custom dependencies. |
Unknown objects fail by default because maxforge cannot safely infer
their port shape. Use --allow-unknown only when you accept
representative 1-inlet/1-outlet metadata and no upper-bound port
rejection; Max must validate the real external.
Core DSL syntax
Patch metadata
patch "Title"
patch "Title" | "Description" | 800x600
Patch metadata is optional and maps to the real Max root patcher
title, description, and rect
fields. Without a declaration, maxforge stores no explicit title, an
empty description, and a 640x480 rectangle; Max may use
the saved filename as its visible window title. Quoted metadata uses
JSON escapes and may contain |.
Objects
name = type [arguments...] [@attribute value...] [at(x, y[, width, height])]
nameis unique inside its patcher and becomes a stable generated box ID.- The catalog contains 320 Max names/aliases with local identity evidence plus the project-owned
maxforgeexternal. - Attributes become Max box properties; structural keys such as
id,maxclass,text, andpatching_rectare reserved. - Write
\@attributewhen the literal@attributemust remain inside anewobjtext string. - Omit
at(x, y)for automatic topological layout. - Use
at(x, y, width, height)when a resized box must round-trip exactly.
osc = cycle~ 440
freq = number @minimum 0 @maximum 127
label = comment "Frequency"
trigger = message "start"
filter = lores~ 1000 0.5 at(240, 180)
Lines beginning with # are comments. Inline comments after a statement are not supported.
Catalog evidence and limits
The 321 catalog entries are compiler metadata, not a complete Max
Object Reference: 239 use a fixed base shape, 45 use explicit
argument rules, and 37 are marked dynamic. Dynamic examples include
poly~, bpatcher, gen~, and
jit.gl.slab; their stored port counts are representative.
- Identity evidence comes from Max 9 reference XML, object indexes/mappings, and saved patchers.
- Embedded Gen, Jitter Gen, and RNBO operators are excluded; they are not ordinary Max patcher boxes.
defaultSizeandcategoryare maxforge layout/grouping values, not Cycling '74 facts.- Use
python3 scripts/audit-object-catalog.pyin a checkout to repeat the local audit.
See the repository's catalog evidence and limits before adding names or relying on dynamic port metadata.
Project externals and abstractions
Declare third-party externals and reusable .maxpat
abstractions in a strict maxforge.config.json. Inline DSL
subpatchers remain embedded and need no configuration.
{
"$schema": "https://2bit.jp/maxforge/schema/config-v1.json",
"schemaVersion": 1,
"project": {
"id": "studio_patchset",
"name": "Studio Patchset"
},
"objects": [{
"name": "vendor.filter~",
"kind": "external",
"ports": {
"mode": "fixed",
"inlets": 2,
"outlets": ["signal", ""]
}
}],
"abstractions": [{
"name": "studio.voice",
"path": "./patchers/studio.voice.maxpat",
"ports": "derive"
}]
}
compile,validate,plan, andbundlesearch upward from the input DSL;--configselects a file explicitly.doctorvalidates root/imported catalogs and reads referenced abstraction metadata before compilation.catalog [query] --jsonlists project metadata or searches built-ins when a query is supplied.- Fixed ports are exact. Bounded argument mode derives counts from one integer argument or argument count. Dynamic ports require a representative shape but do not impose a false upper connection bound.
ports: "derive"counts rootinlet/outletboxes and infers signal outlets from incoming source metadata.- An abstraction name must match its existing
.maxpatfilename. Normal compilation validates but does not embed it, so Max still needs its directory on the search path.bundlecopies referenced declared dependencies instead. - Imported catalogs cannot recursively import more catalogs. Accidental name collisions fail unless
override: trueis explicit. project.idis the stable HTTP persistence namespace. Imported catalogs cannot declare it, and unrelated projects must not share it.
Use the public project config schema and shared catalog schema. A valid catalog is compiler metadata only: it does not install an external, configure Max's search path, or prove runtime availability.
bundle writes the main patch under patchers/,
follows declared abstraction dependencies, copies declared
.mxo/.mxe64 artifacts under
externals/, and writes package-info.json.
Missing dependency paths and destination basename collisions are
errors. Object arguments that refer to arbitrary media or data files
are not inferred and must be packaged separately.
for, if, and arithmetic
Expansion runs before ordinary parsing. This is the feature intended for large families of similar Max objects.
dac = ezdac~ at(430, 420)
for i in 0..7 {
osc_${i} = cycle~ ${110 + i * 27.5} at(${40 + i * 110}, 80)
amp_${i} = *~ 0.125 at(${40 + i * 110}, 140)
osc_${i} -> amp_${i} -> dac
if i < 4 {
meter_${i} = meter~ at(${40 + i * 110}, 210)
amp_${i} -> meter_${i}
}
}
0..7is inclusive.- Use
for i in 0..6 step 2for a stepped range. - Expressions support loop variables, parentheses, comparisons,
+ - * / %, and! && ||. ifsupports an optionalelseblock, including conventional} else {syntax.- Expansion rejects non-finite arithmetic and is capped at 100,000 loop iterations and output lines.
${expr}works in names, arguments, attributes, positions, and connections.- Expressions are numeric only. There are no functions, arrays, strings, or modulo operator.
Connections and ports
a -> b -> c
source[1] -> destination[2]
Ports are zero-indexed from the left. A source suffix selects an outlet;
a destination suffix selects an inlet. maxforge validates indices when
it knows the object shape. It deliberately skips a fabricated upper
bound for dynamicPorts and --allow-unknown
objects; Max remains the final authority for those connections.
Objects such as gate, route, pack,
unpack, trigger, matrix~, and
selector~ derive port counts from their arguments.
Subpatchers
fx = p delay_fx {
input = inlet signal "audio input"
output = outlet signal "audio output"
buffer = tapin~ 500
tap = tapout~ 250
feedback = *~ 0.4
input -> buffer -> tap -> feedback -> buffer
tap -> output
}
Subpatchers may be nested. Only the real Max objects inlet and
outlet are emitted, and they are valid only inside a subpatcher.
The maxforge-only signal modifier marks signal ports; the port
count determines the parent object's visible shape.
Live HTTP control
Install the complete Max package and create one maxforge
object. It starts a service using Max's own Node runtime. All objects
in that Max process share the service. No extra Node installation,
MCP setup, controller patch, or manual port selection is needed.
maxforge live services
maxforge live tools
maxforge live call maxforge_list_patches '{}'
Discovery records live at ~/.maxforge/services/<Max PID>.json.
Use its loopback port and bearer token to read GET /v1/tools
or call POST /v1/tools/<name> with JSON arguments.
The service survives patch and agent disconnects and exits with Max.
Browser requests and remote connections are not supported.
If desired DSL uses a project external or abstraction, add an absolute
@config path to the first object's text.
After catalog edits, call maxforge_reload_catalog and
verify the new digest with maxforge_catalog; restarting
the service is unnecessary. All objects share the first configuration;
conflicting configurations are rejected.
Put a stable project.id in that config when managed state
and ordered edit evidence must survive Max restarts. Maxforge stores
atomic state and a separate append-only NDJSON journal under
~/.maxforge/projects/<project.id>/. Without a project ID,
persistent edit history is disabled rather than merged into a shared
default cache.
Saved paths remain locators. When a path warning is ambiguous, inspect
the append-only identity ledger instead of guessing. Explicit
rekey, merge, and logical forget
decisions affect historical lookup only; they never rewrite live Max
routing, cross scopes, or claim physical erasure.
When the human explicitly requests deletion of retained edit evidence,
close every Max client and use
maxforge_erase_project_history with the exact project ID
and confirmation phrase. It deletes maxforge-owned history chunks and
the identity ledger and clears retained HTTP observations. It does not
delete Max/DSL/config files or desired-state cache, and it cannot
guarantee secure overwrite on SSDs, backups, or filesystem snapshots.
Persistent state and history are single-writer and owned by the Max service. A project writer lease prevents another Max instance from opening the same persistent project. Without a config, desired-state persistence is scoped to the current Max PID; retain complete DSL across Max restarts.
Replace the whole Max package and restart Max for upgrades. Native and bundled service versions must match. Agents use the service's installed action schemas; no separately versioned agent server is required.
Do not apply blind
Use this sequence for every unfamiliar or ambiguous live session:
- 01Use
maxforge_helponly when the compact receipt contract is unavailable, unfamiliar, or in recovery; do not add a redundant call to every normal edit. - 02Call
maxforge_statuswhen process or connection state is uncertain. - 03Before using a custom object, call
maxforge_catalogand confirm the loaded definition. - 04Call
maxforge_list_patches; select an exactpatcherIdandscope. - 05Call
maxforge_inspect_patch. Titles are display metadata, not identities. - 06When recent order matters, call
maxforge_get_live_edit_history. Checksupported,persistence,droppedEvents, session identity, andcomparisonBasis; this is structural evidence, not undo history. - 07If live edits exist, call
maxforge_review_live_changes. Its default summary omits the snapshot, raw rows, duplicate signals, and full DSL; requestdetail: "full"only for exact evidence. - 08Adopt an accepted current baseline with the exact reviewed token, or prepare a concrete next state with
manualChanges: "merge". - 09Call
maxforge_prepare_changewith complete DSL once, or use retainedbaseSourceRefplus small half-open line edits. Review counts, every destructive operation, replacements, warnings, and conflicts. - 10Call
maxforge_apply_prepared_changewith only the one-timereceiptId. Verify the revision, retainsourceRef, and never retry a consumed receipt.
desiredDsl is a complete desired-state declaration for the owned scope. Omitting an existing managed object requests its deletion; it is not an imperative “add these boxes” fragment.
Tool reference
| Tool | Responsibility |
|---|---|
maxforge_help | Agent-facing workflow, setup, recovery, and safety instructions. |
maxforge_status | Server, target, revision, baseline, and effective catalog state. |
maxforge_catalog | Read loaded built-in/custom object metadata and its deterministic digest; not a Max runtime probe. |
maxforge_reload_catalog | Atomically reload configured catalog files without restarting HTTP or dropping Max registrations. |
maxforge_list_patches | List exact registered patcherId/scope targets. |
maxforge_create_patch | Create a new unsaved top-level patch through one controller-capable external. |
maxforge_open_patch | Open an existing Max-host .maxpat, inject one bridge object, and wait for registration. |
maxforge_save_patch | Save or save-as explicitly; apply never saves automatically and overwrite is opt-in. |
maxforge_close_patch | Close a registered patch; dirty-state discard is explicit and opt-in. |
maxforge_inspect_patch | Read boxes, patch cords, nested paths, ownership, and changes without screenshots. |
maxforge_get_live_edit_history | Read bounded, ordered 75 ms structural observations. Arrival time is not an edit timestamp; check drop and comparison metadata. This is not Max undo history or proof of intent. |
maxforge_get_patch_history_identity | Inspect canonical project history identity, aliases, explicit decisions, and logical-forget state without requiring the old patch to be connected. |
maxforge_resolve_patch_history_identity | After human confirmation and source closure, append a rekey, merge, or logical-forget decision. It does not rewrite live routing or erase original evidence. |
maxforge_erase_project_history | After explicit human confirmation and full Max disconnection, delete retained edit-history chunks and the identity ledger and clear their in-memory copy. Secure overwrite and deletion of project source/state files are out of scope. |
maxforge_review_live_changes | Return compact structural evidence and edit clusters by default; full snapshot/raw rows/proposed DSL are opt-in. |
maxforge_adopt_live_changes | Accept a reviewed managed graph with an exact token, advance revision without replay, and return a retained source reference. |
maxforge_prepare_change | Compile or three-way merge complete desired state, retain the bulk plan, and return a compact one-time receipt plus destructive summary. |
maxforge_apply_prepared_change | Consume a receipt, apply without DSL retransmission/recompilation, acknowledge, verify, and retain source state. |
maxforge_get_working_source | Read source metadata, bounded matching snippets, or explicitly requested full retained DSL. |
Live edit history is bounded and collapses edits inside one debounce
window. Project-scoped NDJSON survives Max restart; reconnect creates
a new session baseline instead of comparing across sessions. Saved
paths are locators, not patch identities. Selection and gesture
boundaries remain unavailable. Edit clusters are structural correlations, not recorded gestures or
semantic conclusions. Follow each cluster's changeIndexes
to the exact raw before/after changes. Use
clarificationRecommendedFor to focus reasoning, but ask a
human only when unresolved interpretations would change the next
mutation.
maxforge
The external owns WebSocket transport, stable patch registration,
preflight validation, live structural inspection, and Max SDK mutation.
It does not need node.script, thispatcher, a
separate WebSocket object, routers, or bootstrap patch cords.
maxforge
| Attribute | Meaning |
|---|---|
@patcher_id | Stable HTTP routing identity. Do not derive it from the window title. |
@scope | Exact managed ownership namespace. |
@config | Optional absolute catalog/project configuration path, shared by this Max process. |
@revision_state | Persisted optimistic-concurrency revision. |
Managed scripting names follow
maxforge_<scope>_obj_<dsl-name>. The external must not
modify boxes outside that exact namespace.
Any registered object can service maxforge_create_patch.
Closing the original patch does not stop other patches or the service.
Ambiguous state requires inspection
| Situation | Required action |
|---|---|
| Apply timed out | Do not retry. Call maxforge_help with recovery, inspect the live patch, and determine whether Max applied the revision. |
| Service failure | Restart Max, then status → list → inspect. Provide exact complete currentDsl if the previous baseline was not persisted. |
| Max or the patch restarted | Wait for registration, then status → list → inspect before compiling another plan. |
| Managed manual edit detected | Review first. Adopt the accepted current baseline with its exact token and retain returned sourceRef, or prepare a concrete next state with manualChanges: "merge". |
| Unmanaged manual edit detected | Review it. Unmanaged-only edits are reported but do not block managed mutation. |
Install operating instructions
Skills do not install the npm package or Max external. They give compatible agents repository-specific instructions.
npx skills add bbb-max-externals/maxforge --list
npx skills add bbb-max-externals/maxforge --skill maxforge
npx skills add bbb-max-externals/maxforge --skill maxforge-live
Use maxforge for offline authoring and compilation. Use maxforge-live for target selection, plan review, acknowledgement, inspection, and recovery discipline.
Current limits and security boundary
- Live synchronization is experimental protocol version 1.
- Project catalog entries describe compiler serialization and ports. They do not prove that the external binary, abstraction search path, architecture, or dependencies exist on the Max host.
- A plan is fully validated before mutation, but arbitrary Max SDK mutation has no implemented rollback after a runtime failure.
- Inspection is structural. It does not serialize arbitrary object attributes, DSP values, UI values, patchline colors, hidden state, or line midpoints.
- Reverse compilation preserves structure and explicit positions; it does not reproduce the exact original DSL source text.
- HTTP and WebSocket listen only on loopback with a generated token. Tokens are not saved in Max patches. Browser origins are rejected; LAN mode is not available.
- The ownership marker is the reserved scripting-name namespace. The native consumer treats such boxes as owned, but HTTP reconciliation rejects newly introduced reserved identities because inspection lacks enough metadata for safe adoption.
- Reconciliation preserves observed edits to existing managed identities. Arbitrary object attributes omitted from structural inspection cannot be merged.
- Moving a managed box to another patcher path is not merged as a move; express the intended delete/add in complete DSL and resolve the conflict explicitly.
- HTTP apply plans carry the inspected structure token. If a box or cord changes before native mutation,
maxforgerejects the stale plan. - Only CI-produced versioned macOS release archives are Developer ID signed and notarized. Local builds remain ad-hoc signed.
Detailed references
This page is the operational guide. Use the repository documents below for exact schemas and contracts.