[{"data":1,"prerenderedAt":740},["ShallowReactive",2],{"navigation_docs":3,"-security-known-issues":144,"-security-known-issues-surround":735},[4,35,84,109,124,134,139],{"title":5,"path":6,"stem":7,"children":8,"page":34},"Get Started","\u002Fget-started","1.get-started",[9,14,19,24,29],{"title":10,"path":11,"stem":12,"icon":13},"Introduction","\u002Fget-started\u002Fintroduction","1.get-started\u002F1.introduction","i-lucide-house",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fget-started\u002Finstallation","1.get-started\u002F2.installation","i-lucide-download",{"title":20,"path":21,"stem":22,"icon":23},"Quick start","\u002Fget-started\u002Fquickstart","1.get-started\u002F3.quickstart","i-lucide-rocket",{"title":25,"path":26,"stem":27,"icon":28},"Project structure","\u002Fget-started\u002Fproject-structure","1.get-started\u002F4.project-structure","i-lucide-folder-tree",{"title":30,"path":31,"stem":32,"icon":33},"Dashboard","\u002Fget-started\u002Fdashboard","1.get-started\u002F5.dashboard","i-lucide-layout-dashboard",false,{"title":36,"path":37,"stem":38,"children":39,"page":34},"Concepts","\u002Fconcepts","2.concepts",[40,45,50,55,60,65,69,74,79],{"title":41,"path":42,"stem":43,"icon":44},"Architecture","\u002Fconcepts\u002Farchitecture","2.concepts\u002F1.architecture","i-lucide-layers",{"title":46,"path":47,"stem":48,"icon":49},"Machines","\u002Fconcepts\u002Fmachines","2.concepts\u002F2.machines","i-lucide-server",{"title":51,"path":52,"stem":53,"icon":54},"Colony & users","\u002Fconcepts\u002Fcolony","2.concepts\u002F3.colony","i-lucide-users",{"title":56,"path":57,"stem":58,"icon":59},"Transport","\u002Fconcepts\u002Ftransport","2.concepts\u002F4.transport","i-lucide-network",{"title":61,"path":62,"stem":63,"icon":64},"Configuration","\u002Fconcepts\u002Fconfiguration","2.concepts\u002F5.configuration","i-lucide-settings",{"title":66,"path":67,"stem":68,"icon":23},"Deploy","\u002Fconcepts\u002Fdeploy","2.concepts\u002F6.deploy",{"title":70,"path":71,"stem":72,"icon":73},"Groups","\u002Fconcepts\u002Fgroups","2.concepts\u002F7.groups","i-lucide-boxes",{"title":75,"path":76,"stem":77,"icon":78},"Templates","\u002Fconcepts\u002Ftemplates","2.concepts\u002F8.templates","i-lucide-package-plus",{"title":80,"path":81,"stem":82,"icon":83},"Existing projects","\u002Fconcepts\u002Fexisting-projects","2.concepts\u002F9.existing-projects","i-lucide-folder-open",{"title":85,"path":86,"stem":87,"children":88,"page":34},"Remote","\u002Fremote","3.remote",[89,94,99,104],{"title":90,"path":91,"stem":92,"icon":93},"Remote machines","\u002Fremote\u002Foverview","3.remote\u002F1.overview","i-lucide-cloud",{"title":95,"path":96,"stem":97,"icon":98},"Remote deploy","\u002Fremote\u002Fdeploy","3.remote\u002F2.deploy","i-lucide-send",{"title":100,"path":101,"stem":102,"icon":103},"CI","\u002Fremote\u002Fci","3.remote\u002F3.ci","i-lucide-workflow",{"title":105,"path":106,"stem":107,"icon":108},"Tunnels","\u002Fremote\u002Ftunnel","3.remote\u002F4.tunnel","i-lucide-cable",{"title":110,"path":111,"stem":112,"children":113,"page":34},"Security","\u002Fsecurity","4.security",[114,119],{"title":115,"path":116,"stem":117,"icon":118},"Security model","\u002Fsecurity\u002Fmodel","4.security\u002F1.model","i-lucide-shield",{"title":120,"path":121,"stem":122,"icon":123},"Known issues","\u002Fsecurity\u002Fknown-issues","4.security\u002F2.known-issues","i-lucide-triangle-alert",{"title":125,"path":126,"stem":127,"children":128,"page":34},"Reference","\u002Freference","5.reference",[129],{"title":130,"path":131,"stem":132,"icon":133},"CLI reference","\u002Freference\u002Fcli","5.reference\u002F1.cli","i-lucide-terminal",{"title":135,"path":136,"stem":137,"icon":138},"Changelog","\u002Fchangelog","changelog","i-lucide-history",{"title":140,"path":141,"stem":142,"icon":143},"Communities","\u002Fcommunities","communities","i-lucide-heart-handshake",{"id":145,"title":120,"body":146,"description":727,"extension":728,"links":729,"meta":730,"navigation":731,"path":121,"seo":732,"stem":122,"__hash__":734},"docs\u002F4.security\u002F2.known-issues.md",{"type":147,"value":148,"toc":713},"minimark",[149,153,163,174,234,292,297,350,356,378,387,391,398,453,466,470,478,489,493,501,512,516,523,526,530,537,568,571,575,583,597,613,616,623,630,641,662,672,676,709],[150,151,152],"p",{},"Tracked gaps that are deliberately deferred. Each entry says what is missing,\nwhy, the impact, the path forward, and where the code lives.",[154,155,157,158,162],"h2",{"id":156},"_1-remote-run-stack-deploys-stack-builds-are-open","1. Remote ",[159,160,161],"code",{},"run: stack"," deploys; stack builds are open",[150,164,165,169,170,173],{},[166,167,168],"strong",{},"Pillar:"," Deploy · ",[166,171,172],{},"Status:"," routing, zero-downtime, and destroy implemented",[150,175,176,179,180,183,184,187,188,191,192,195,196,199,200,202,203,206,207,210,211,214,215,218,219,222,223,226,227,230,231,233],{},[166,177,178],{},"What works now."," A ",[159,181,182],{},"run: container"," project with a ",[159,185,186],{},"machine:"," builds on the\nmachine: the client packages the build context (",[159,189,190],{},"build.run",", a gzipped tar with\nthe context's ",[159,193,194],{},".dockerignore"," applied), the worker unpacks it into a temporary\ndirectory, runs the same builder as the local path, and starts the container\nwith volumes, resources, restart policy, and log rotation. A ",[159,197,198],{},"run: compose","\nproject with a ",[159,201,186],{}," resolves the compose file (",[159,204,205],{},"docker compose config",",\nall ",[159,208,209],{},"deploy.files"," layers, with ",[159,212,213],{},"deploy.profiles","), builds every service that\ndeclares ",[159,216,217],{},"build:"," on the machine (each service's context is uploaded and built\nindividually, then rewritten to the produced ",[159,220,221],{},"image:","), and ships the file to\nthe worker, which runs ",[159,224,225],{},"docker compose up -d",". Image-only services pull as\nbefore. ",[159,228,229],{},"deploy.build_services: false"," is refused for a file that still\ndeclares ",[159,232,217],{},", the machine has no source tree to run it from.",[150,235,236,237,183,239,241,242,244,245,247,248,251,252,255,256,259,260,263,264,267,268,271,272,275,276,279,280,283,284,287,288,291],{},"A ",[159,238,161],{},[159,240,186],{}," sends the stack file (single file;\n",[159,243,209],{}," layering is refused) to the worker, which refuses any service\nthat declares ",[159,246,217],{}," (push the image first, for example with\n",[159,249,250],{},"deploy.registry","), stores the file under the machine's project directory, and\nruns ",[159,253,254],{},"docker stack deploy --with-registry-auth",". Domains are routed through the\nmachine's Caddy at each stack service's published host port (the domain names\nthe service with ",[159,257,258],{},"service:"," and its container port with ",[159,261,262],{},"port:","). Every service\ngets ant's managed\u002Fapp labels, so tasks show up in ",[159,265,266],{},"ant nest containers list","\nand ",[159,269,270],{},"ant trail status",", and ",[159,273,274],{},"ant trail logs \u003Cdeployment> [service]"," reads a\ntask's logs (name the service when the stack has several). The machine must be a\nSwarm manager, ",[159,277,278],{},"ant nest swarm init --machine M"," sets up a single-node one;\n",[159,281,282],{},"ant nest swarm status"," and ",[159,285,286],{},"ant nest swarm leave --force"," manage it. Swarm is a\nserver feature: ant never initializes a swarm on this host.\n",[159,289,290],{},"zero_downtime"," patches a Swarm rolling-update policy (start-first, rollback on\nfailure, convergence wait); services that publish ports in host mode fall back\nto stop-first with a warning, since two tasks cannot bind the same host port on\na single node.",[150,293,294],{},[166,295,296],{},"What's missing.",[298,299,300,316,322],"ul",{},[301,302,303,306,307,309,310,312,313,315],"li",{},[166,304,305],{},"Stack builds",": services that declare ",[159,308,217],{}," are refused; prebuild and\npush, or use ",[159,311,182],{},"\u002F",[159,314,198],{},".",[301,317,318,321],{},[166,319,320],{},"Stack rollback",": refused, like compose: a stack's release is a file that\nnames several images, not one recorded image. Pin the previous image in the\nstack file and redeploy (Swarm rolls it out).",[301,323,324,327,328,331,332,335,336,331,339,331,342,345,346,349],{},[166,325,326],{},"Cluster and service operations",": the common ones are available\n(",[159,329,330],{},"nest swarm nodes",", ",[159,333,334],{},"node promote|demote|drain|activate|pause|rm",",\n",[159,337,338],{},"join-token",[159,340,341],{},"services",[159,343,344],{},"service ps|logs|scale|restart|update|rm",").\nAnything beyond them (overlay\u002Fnetwork administration, swarm configs and\nsecrets, and the rest of ",[159,347,348],{},"docker service update",") stays docker commands on\nthe machine.",[150,351,352,355],{},[159,353,354],{},"ant trail destroy --machine M"," stops and removes the deployment's workload on\nthe machine (container, compose project, or stack) and clears its Caddy routes.\nThe machine's images are left in place, so a redeploy reuses the build cache;\nthe local command deletes ant-built images instead.",[298,357,358],{},[301,359,360,363,364,331,367,331,370,373,374,377],{},[166,361,362],{},"Compose build features",": ",[159,365,366],{},"build.target",[159,368,369],{},"build.secrets",[159,371,372],{},"build.ssh",", and\n",[159,375,376],{},"build.additional_contexts"," are refused (not silently ignored); prebuild and\nreference the image instead.",[150,379,380,383,384,386],{},[166,381,382],{},"Note."," The resolved compose file travels inside one RPC request frame,\ncapped at 2 MiB (response frames are capped at 8 MiB); a larger compose file\nfails. Build logs from ",[159,385,190],{}," are capped at 1 MiB (the tail is returned),\nand the worker bounds context extraction to 8 GiB and rejects unsafe archive\nentries.",[154,388,390],{"id":389},"_2-registry-credentials-travel-with-the-deploy","2. Registry credentials travel with the deploy",[150,392,393,169,395,397],{},[166,394,168],{},[166,396,172],{}," implemented",[150,399,400,402,403,405,406,409,410,413,414,417,418,421,422,425,426,331,428,373,431,434,435,438,439,442,443,445,446,449,450,452],{},[166,401,178],{}," With ",[159,404,250],{}," set, a source-built remote deploy\nbuilds on this machine, tags the image ",[159,407,408],{},"\u003Chost>\u002F\u003Crepository>\u002F\u003Capp>:\u003Crelease>",",\nand pushes it (",[159,411,412],{},"docker login --password-stdin"," when ",[159,415,416],{},"ANT_REGISTRY_PASSWORD"," is\nset); the machine then pulls the qualified reference on ",[159,419,420],{},"deploy.run",". The same\npassword travels with the deploy over the encrypted transport and is\nmaterialized on the machine as a short-lived ",[159,423,424],{},"DOCKER_CONFIG"," (0700 directory,\n0600 file) for every command that can pull: ",[159,427,420],{},[159,429,430],{},"compose.apply",[159,432,433],{},"stack.apply"," (where ",[159,436,437],{},"--with-registry-auth"," forwards it to the nodes). The\ndirectory is removed when the operation ends; ant never stores the credential\nin a config or on disk. For an explicit ",[159,440,441],{},"--image ghcr.io\u002F..."," pull without\n",[159,444,250],{},", the host is parsed from the reference and the username comes\nfrom ",[159,447,448],{},"ANT_REGISTRY_USERNAME"," (both with ",[159,451,416],{},").",[150,454,455,458,459,312,462,465],{},[166,456,457],{},"Limits."," The credential crosses the wire on each deploy, the transport\nencrypts the frame, and the value is handled like any other runtime secret.\nAnyone who may deploy can therefore pull the configured registry's private\nimages; ",[159,460,461],{},"deployer",[159,463,464],{},"ci"," are already root-equivalent by design. The local image\nbuilt for the push is not pruned by the local pruner yet.",[154,467,469],{"id":468},"_3-the-dashboard-is-local-only-by-design-no-multi-user-auth","3. The dashboard is local-only by design (no multi-user auth)",[150,471,472,474,475,477],{},[166,473,168],{}," Collab · ",[166,476,172],{}," by design",[150,479,480,481,484,485,488],{},"Each operator runs their own dashboard on their own laptop, against their own\n",[159,482,483],{},"~\u002F.ant",", and it is never hosted on a server. Machine access is governed by\neach machine's colony roster and roles, so a user's dashboard can only act on\nthe machines their account can reach. There is no shared-login model to\nsupport: an operator editing their own files is the trust boundary. The\nnon-loopback password (",[159,486,487],{},"ant ui passwd",") exists only as a safety net for\nreaching your own dashboard from another device, not as a hosted mode.",[154,490,492],{"id":491},"_4-the-audit-log-is-local-to-the-machine","4. The audit log is local to the machine",[150,494,495,497,498,500],{},[166,496,168],{}," Security · ",[166,499,172],{}," implemented; off-host shipping deferred",[150,502,503,504,507,508,511],{},"Privileged actions (deploy, route, roster, volume, tunnel) are appended to a\nhash-chained log, and ",[159,505,506],{},"ant nest audit"," reports whether the chain verifies, so an\nedited or removed line is detected. The log rotates at 8 MiB (the prior segment\nis kept as ",[159,509,510],{},"audit.log.1"," and is independently verifiable); reading the log\nrequires the admin role. The log lives on the machine and is not shipped\noff-host: an operator with root can still delete the whole file. Forwarding a\ncopy to a collector is future work.",[154,513,515],{"id":514},"_5-no-trust-on-first-use-for-a-machine-nodeid","5. No trust-on-first-use for a machine NodeID",[150,517,518,497,520,522],{},[166,519,168],{},[166,521,172],{}," partial",[150,524,525],{},"Invite tokens pin the machine NodeID; direct registration does not. Pinning on\nfirst contact is future work.",[154,527,529],{"id":528},"_6-no-secret-store","6. No secret store",[150,531,532,497,534,536],{},[166,533,168],{},[166,535,172],{}," accepted",[150,538,539,540,543,544,547,548,551,552,555,556,559,560,563,564,567],{},"Ant has no vault of its own: build-time secrets are read from the operator's\nenvironment and passed to the builder for one build, and runtime secrets are\ninjected as container environment variables. Nothing is encrypted at rest by\nant, and no secret is ever written to ",[159,541,542],{},"ant.yaml",", the lockfile, or the machine\nstate. A secret may name a ",[166,545,546],{},"provider"," (",[159,549,550],{},"from: op:\u002F\u002F…"," for 1Password, a generic\n",[159,553,554],{},"cmd:…"," for Vault\u002FAWS\u002F",[159,557,558],{},"pass",", or ",[159,561,562],{},"env:…","), resolved through the operator's CLI\nat deploy time so the value still never lands in ant. ",[159,565,566],{},"deploy.secrets"," entries\nwith no environment value are reported as warnings, not errors, so a typo is\nvisible but not fatal.",[150,569,570],{},"If you need rotation, audit, or per-environment access control for secrets, keep\nthem in your existing secret manager and export them into the deploy\nenvironment",[154,572,574],{"id":573},"_7-worker-upgrades-are-a-re-provision","7. Worker upgrades are a re-provision",[150,576,577,579,580,582],{},[166,578,168],{}," Ops · ",[166,581,172],{}," by design for now",[150,584,585,588,589,592,593,596],{},[159,586,587],{},"ant-worker"," has no self-update path: it runs unprivileged and cannot write\n",[159,590,591],{},"\u002Fusr\u002Flocal\u002Fbin"," or restart its own unit. Upgrading a machine re-runs the\nbootstrap over SSH, which preserves the machine identity and ",[159,594,595],{},"state.json","\n(roster, invites):",[598,599,604],"pre",{"className":600,"code":601,"language":602,"meta":603,"style":603},"language-sh shiki shiki-themes material-theme-lighter github-light github-dark","ant nest bootstrap --machine prod --host 192.0.2.10 --ssh-key ~\u002F.ssh\u002Fid_ed25519\n","sh","",[159,605,606],{"__ignoreMap":603},[607,608,611],"span",{"class":609,"line":610},"line",1,[607,612,601],{},[150,614,615],{},"The client and worker carry an RPC protocol version and refuse to operate\nagainst a mismatched peer, so upgrade both sides together (CLI first is fine:\nan old worker answers the new client with a version error until it is\nre-provisioned).",[154,617,619,620,622],{"id":618},"_8-remote-zero_downtime-containers-and-stacks-yes-compose-later","8. Remote ",[159,621,290],{},": containers and stacks yes, compose later",[150,624,625,169,627,629],{},[166,626,168],{},[166,628,172],{}," implemented; remote compose still staged",[150,631,236,632,634,635,637,638,640],{},[159,633,182],{}," deploy with ",[159,636,290],{}," and at least one domain stages\nthe new container beside the running one on an ephemeral host port,\nhealth-gates it, moves the app's Caddy routes to that port, and only then\nparks-and-removes the old container and renames the new one into service. The\npublished port survives the rename, so the routes keep serving throughout. The\nswap is one ",[159,639,420],{}," call (routes travel in the payload), so a failure\nbefore the route move leaves the old container and its routes untouched.",[150,642,236,643,634,645,647,648,331,651,654,655,658,659,661],{},[159,644,161],{},[159,646,290],{}," patches a Swarm rolling-update\npolicy: ",[159,649,650],{},"order: start-first",[159,652,653],{},"failure_action: rollback",", and a convergence\nwait, so Swarm starts new tasks before stopping old ones while the published\nport (held by the routing mesh) keeps serving. Services that publish ports in\n",[159,656,657],{},"mode: host"," cannot overlap two tasks on a single node, so those get stop-first\nwith a warning; services without a healthcheck warn that rollback relies on\ntask state. Routing warns too when a routed service publishes in ",[159,660,657],{},"\non a multi-node swarm: that port exists only on the node running the task,\nwhile Caddy runs on the manager.",[150,663,664,665,668,669,671],{},"The container path falls back to the staged swap (with a warning) when there is\nno domain to move, no running predecessor, or a writable mount that must not be\nattached to a second container. Remote ",[159,666,667],{},"compose"," still uses the staged swap;\n",[159,670,225],{}," has no native rolling update, so it needs the same\nephemeral-port + Caddy-repoint pattern per service.",[154,673,675],{"id":674},"tracked-on-other-branches","Tracked on other branches",[298,677,678,696],{},[301,679,680,683,684,687,688,691,692,695],{},[166,681,682],{},"SSH over iroh",": the TCP-forwarding primitive now exists (",[159,685,686],{},"ant nest tunnel",",\na raw iroh stream), so forwarding a machine's ",[159,689,690],{},"sshd"," is the next step;\nprovisioning still uses the host's standard SSH (with ",[159,693,694],{},"--cloud-init"," as the\nno-SSH fallback).",[301,697,698,363,701,703,704,708],{},[166,699,700],{},"Container-to-container across machines",[159,702,686],{}," lets an operator\nreach a peer machine's published ports over iroh. A ",[705,706,707],"em",{},"worker"," dialing another\nworker would need the caller's machine NodeID on the peer's roster, a\nmachine-to-machine trust model that does not exist yet (the roster holds\naccount NodeIDs).",[710,711,712],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":603,"searchDepth":714,"depth":714,"links":715},2,[716,718,719,720,721,722,723,724,726],{"id":156,"depth":714,"text":717},"1. Remote run: stack deploys; stack builds are open",{"id":389,"depth":714,"text":390},{"id":468,"depth":714,"text":469},{"id":491,"depth":714,"text":492},{"id":514,"depth":714,"text":515},{"id":528,"depth":714,"text":529},{"id":573,"depth":714,"text":574},{"id":618,"depth":714,"text":725},"8. Remote zero_downtime: containers and stacks yes, compose later",{"id":674,"depth":714,"text":675},"Deferred work, with why and the path forward.","md",null,{},{"icon":123},{"title":120,"description":733},"Tracked gaps in Ant that are deliberately deferred, and where the code lives.","Z3NoYWbIrRjsi_Ls1oM9c4ASrtBNWjDu94GgxvdYlFU",[736,738],{"title":115,"path":116,"stem":117,"description":737,"icon":118,"children":-1},"How Ant authenticates, authorizes, and limits what a peer can do.",{"title":130,"path":131,"stem":132,"description":739,"icon":133,"children":-1},"Every command group, global flag, and the common flows.",1791494557238]