[{"data":1,"prerenderedAt":1060},["ShallowReactive",2],{"navigation_docs":3,"-security-model":144,"-security-model-surround":1055},[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":115,"body":146,"description":152,"extension":1048,"links":1049,"meta":1050,"navigation":1051,"path":116,"seo":1052,"stem":117,"__hash__":1054},"docs\u002F4.security\u002F1.model.md",{"type":147,"value":148,"toc":1032},"minimark",[149,153,158,190,193,216,220,232,442,468,474,493,497,537,541,605,610,613,677,696,700,917,921,932,936,983,987],[150,151,152],"p",{},"How Ant authenticates, authorizes, and limits what a peer can do.",[154,155,157],"h2",{"id":156},"identity","Identity",[159,160,161,183],"ul",{},[162,163,164,165,169,170,174,175,178,179,182],"li",{},"Every principal holds an ed25519 keypair. The ",[166,167,168],"strong",{},"public key is the NodeID",", in\nbase32 (no padding). Account keys live at ",[171,172,173],"code",{},"~\u002F.ant\u002Fidentity","; a machine's key\nlives on the machine (",[171,176,177],{},"~\u002F.ant\u002Fworker\u002Fagent-identity",", or wherever the\nworker's ",[171,180,181],{},"--identity"," points).",[162,184,185,186,189],{},"The transport binds an endpoint with the identity's seed and ",[166,187,188],{},"verifies the\nbound endpoint id equals the NodeID",", so a peer cannot present a key it does\nnot hold.",[154,191,56],{"id":192},"transport",[159,194,195,202,205],{},[162,196,197,198,201],{},"iroh = QUIC + TLS 1.3 with ",[166,199,200],{},"mutual authentication",": both sides prove\npossession of their key. Dialing is by NodeID; no open ports, no host\ncertificates.",[162,203,204],{},"A self-hosted relay, when configured, only forwards encrypted bytes.",[162,206,207,208,211,212,215],{},"Private keys are ",[171,209,210],{},"0600",". The account's portable credential bundle is\n",[166,213,214],{},"passphrase-encrypted by default"," (PBKDF2-HMAC-SHA256, 210k iterations,\nAES-256-GCM), and load re-checks that the key matches the profile NodeID.",[154,217,219],{"id":218},"authorization-the-daemon-is-the-authority","Authorization: the daemon is the authority",[150,221,222,223,227,228,231],{},"Mutual TLS authenticates the ",[224,225,226],"em",{},"channel","; it does ",[166,229,230],{},"not"," authorize the caller.\nThe machine daemon resolves the caller's NodeID against its roster and enforces\na method → capability table:",[233,234,235,248],"table",{},[236,237,238],"thead",{},[239,240,241,245],"tr",{},[242,243,244],"th",{},"Method",[242,246,247],{},"Requires",[249,250,251,266,276,325,368,378],"tbody",{},[239,252,253,263],{},[254,255,256,259,260],"td",{},[171,257,258],{},"ping",", ",[171,261,262],{},"whoami",[254,264,265],{},"public",[239,267,268,273],{},[254,269,270],{},[171,271,272],{},"invites.redeem",[254,274,275],{},"public, gated by a signed invite token",[239,277,278,319],{},[254,279,280,259,283,259,286,259,289,259,292,259,295,259,298,259,301,259,304,259,307,259,310,259,313,259,316],{},[171,281,282],{},"users.list",[171,284,285],{},"containers.list",[171,287,288],{},"containers.logs",[171,290,291],{},"images.list",[171,293,294],{},"volumes.list",[171,296,297],{},"metrics.get",[171,299,300],{},"caddy.status",[171,302,303],{},"tools.probe",[171,305,306],{},"swarm.status",[171,308,309],{},"swarm.nodes",[171,311,312],{},"swarm.services",[171,314,315],{},"swarm.service.tasks",[171,317,318],{},"swarm.service.logs",[254,320,321,324],{},[171,322,323],{},"read"," (viewer+)",[239,326,327,365],{},[254,328,329,259,332,259,335,259,338,259,341,259,344,259,347,259,350,259,353,259,356,259,359,259,362],{},[171,330,331],{},"users.add",[171,333,334],{},"users.setRole",[171,336,337],{},"users.remove",[171,339,340],{},"users.update",[171,342,343],{},"invites.revoke",[171,345,346],{},"state.export",[171,348,349],{},"audit.list",[171,351,352],{},"swarm.init",[171,354,355],{},"swarm.leave",[171,357,358],{},"swarm.node.update",[171,360,361],{},"swarm.node.rm",[171,363,364],{},"swarm.joinToken",[254,366,367],{},"colony manage, and the caller must be allowed to grant\u002Fremove\u002Fmodify the target's role",[239,369,370,375],{},[254,371,372],{},[171,373,374],{},"state.import",[254,376,377],{},"ownership (owner only)",[239,379,380,439],{},[254,381,382,259,385,259,388,259,391,259,394,259,397,259,400,259,403,259,406,259,409,259,412,259,415,259,418,259,421,259,424,259,427,259,430,259,433,259,436],{},[171,383,384],{},"deploy.run",[171,386,387],{},"deploy.rollback",[171,389,390],{},"deploy.commit",[171,392,393],{},"image.load",[171,395,396],{},"build.run",[171,398,399],{},"compose.apply",[171,401,402],{},"compose.down",[171,404,405],{},"stack.apply",[171,407,408],{},"stack.down",[171,410,411],{},"route.apply",[171,413,414],{},"containers.action",[171,416,417],{},"containers.remove",[171,419,420],{},"volumes.remove",[171,422,423],{},"swarm.service.scale",[171,425,426],{},"swarm.service.restart",[171,428,429],{},"swarm.service.update",[171,431,432],{},"swarm.service.rm",[171,434,435],{},"handover.export",[171,437,438],{},"tunnel.open",[254,440,441],{},"deploy (deployer, ci, admin, owner)",[150,443,444,445,448,449,452,453,456,457,460,461,464,465,467],{},"Role capabilities: ",[171,446,447],{},"owner"," (all) · ",[171,450,451],{},"admin"," (all but ownership transfer) ·\n",[171,454,455],{},"deployer"," (read, deploy, osuser) · ",[171,458,459],{},"viewer"," (read) · ",[171,462,463],{},"ci"," (read, deploy).\nOwnership transfer (",[171,466,374],{},") is owner-only.",[150,469,470,473],{},[166,471,472],{},"Fail closed."," With no roster and no boot owner, every gated method is refused.\nThe first owner is seeded out of band:",[159,475,476,486],{},[162,477,478,481,482,485],{},[171,479,480],{},"ant-worker --owner \u003Cnodeid>"," (repeatable), the bootstrap\u002Fprovisioning owner,\nemitted by ",[171,483,484],{},"ant nest bootstrap","; or",[162,487,488,489,492],{},"writing the owner into the machine's ",[171,490,491],{},"state.json"," as root.",[154,494,496],{"id":495},"invites","Invites",[159,498,499,502,519,528],{},[162,500,501],{},"An invite is signed by the inviter's account key and carries the destination\nmachine NodeID, the role, and an expiry.",[162,503,504,507,508,510,511,514,515,518],{},[166,505,506],{},"Redemption is server-side"," (",[171,509,272],{},"): the daemon verifies the\nsignature, expiry, that the invite is for ",[224,512,513],{},"this"," machine, the inviter's\n",[166,516,517],{},"current"," role, and the grant matrix, and that the nonce is unused.",[162,520,521,524,525,527],{},[166,522,523],{},"Single-use"," is enforced by the daemon (",[171,526,491],{}," keeps consumed nonces\nuntil their expiry). The invitee need not be on the roster yet.",[162,529,530,533,534,536],{},[166,531,532],{},"Revocable",": ",[171,535,343],{}," (colony manage) records a nonce as revoked\nuntil its expiry, and redemption rejects it, so a leaked token can be killed.",[154,538,540],{"id":539},"least-privilege-on-the-machine","Least privilege on the machine",[159,542,543,569,584,602],{},[162,544,545,546,549,550,553,554,557,558,259,561,564,565,568],{},"The worker runs ",[166,547,548],{},"unprivileged and never invokes sudo",". Privileged work on a\nmachine is operator-run and executes ",[166,551,552],{},"exact argv, never a shell",":\n",[171,555,556],{},"ant nest reconcile --apply"," runs as root, and the SSH provisioning path\n(",[171,559,560],{},"ant nest machines add --host",[171,562,563],{},"ant nest bootstrap --host",") runs its\nprivileged steps on the target through the SSH user's ",[171,566,567],{},"sudo",".",[162,570,571,572,575,576,579,580,583],{},"Docker calls use ",[171,573,574],{},"exec.Command"," with argument slices, not ",[171,577,578],{},"sh -c",". The image\nis passed after ",[171,581,582],{},"--",", so a crafted reference cannot be read as a flag.",[162,585,586,589,590,593,594,597,598,601],{},[166,587,588],{},"Server-side validation",": container names, compose project names, and\nnetwork names must match ",[171,591,592],{},"^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$","; image refs may\nnot start with ",[171,595,596],{},"-"," or contain whitespace. This blocks path traversal\n(",[171,599,600],{},"..\u002F…",") and flag injection.",[162,603,604],{},"Container actions address the container by exact name only.",[606,607,609],"h3",{"id":608},"the-deploy-capability-is-root-equivalent-on-the-machine","The deploy capability is root-equivalent on the machine",[150,611,612],{},"This is the most important thing to understand before inviting someone:",[159,614,615,655,671],{},[162,616,617,619,620,622,623,626,627,630,631,638,639,642,643,646,647,650,651,654],{},[171,618,455],{}," and ",[171,621,463],{}," can run ",[166,624,625],{},"any image",", with ",[166,628,629],{},"arbitrary environment"," and\n",[166,632,633,634,637],{},"arbitrary ",[171,635,636],{},"-v"," mounts",". A deployer can mount the worker's own state\ndirectory (",[171,640,641],{},"~ant\u002F.ant",") into a container and read the machine's private\nidentity, or mount the host filesystem. In ",[171,644,645],{},"group"," Docker mode the worker is\nin the ",[171,648,649],{},"docker"," group, which is host root; in ",[171,652,653],{},"rootless"," mode it is full\ncontrol of the worker's unix user (including its NodeID key).",[162,656,657,658,660,661,663,664,667,668,670],{},"Treat ",[171,659,455],{},"\u002F",[171,662,463],{}," as ",[166,665,666],{},"machine-level trust",", not as a read-mostly role.\nGive the admin\u002Fowner roles to people you would trust with SSH root on that\nmachine, and prefer ",[171,669,459],{}," for read-only access.",[162,672,673,676],{},[171,674,675],{},"run: compose"," service images can also declare arbitrary mounts and\nprivileges; the compose file comes from a project config the deployer\ncontrols.",[150,678,679,680,259,682,259,685,259,688,691,692,695],{},"The role matrix (",[171,681,323],{},[171,683,684],{},"deploy",[171,686,687],{},"colony.manage",[171,689,690],{},"ownership.transfer",",\n",[171,693,694],{},"osuser",") is enforced server-side, and the worker never runs privileged host\ncommands, but container execution itself is the escape hatch, as with any\nDocker-based deploy tool.",[606,697,699],{"id":698},"routes-and-ports","Routes and ports",[159,701,702,713,726,752,770,784,845,868,879,898],{},[162,703,704,705,708,709,712],{},"A published port binds ",[166,706,707],{},"loopback"," by default, so a deployed app is reachable\nthrough the machine's reverse proxy (and on the machine) but not from the\nnetwork. ",[171,710,711],{},"deploy.publish: all"," opts into binding every interface.",[162,714,715,717,718,721,722,725],{},[171,716,411],{}," accepts only ",[166,719,720],{},"valid hostnames"," (no wildcards, ports, schemes,\nor paths), only ",[166,723,724],{},"loopback upstreams"," (the machine's own containers), and\nrejects a host (or host+path) already routed by another app, so a deployer\ncannot shadow or hijack a teammate's domain.",[162,727,728,729,732,733,507,736,739,740,743,744,747,748,751],{},"The worker's Caddy admin API is bound to ",[171,730,731],{},"127.0.0.1:2019"," with an ",[166,734,735],{},"origin\nallowlist",[171,737,738],{},"origins 127.0.0.1:2019 localhost:2019","), so a browser request\ncarrying a foreign ",[171,741,742],{},"Origin"," (a page on another domain that resolves to\nloopback, i.e. DNS rebinding) cannot reprogram routing, and a request with a\nforeign ",[171,745,746],{},"Host"," is refused. On a multi-user host, prefer the distribution's\n",[171,749,750],{},"caddy","-only unix socket instead.",[162,753,754,755,758,759,761,762,765,766,769],{},"The dashboard is ",[166,756,757],{},"operator-local by design",": one dashboard per laptop,\nreading that operator's own files, never hosted on a server. It binds\n",[166,760,707],{}," and has no authentication there (the trust boundary is the local\nmachine). Binding it to a ",[166,763,764],{},"non-loopback"," address is an escape hatch for\nreaching your own dashboard from another device: it is refused unless a\npassword is set (",[171,767,768],{},"ant ui passwd","), and is then wrapped in HTTP Basic auth and\nshould sit behind TLS. A successful login issues a signed, HttpOnly session\ncookie whose key derives from the password hash, so the expensive hash runs at\nlogin rather than on every request, and changing the password invalidates\nexisting sessions.",[162,771,772,773,776,777,779,780,783],{},"On a loopback bind the dashboard also applies a ",[166,774,775],{},"Host allow-list"," (rejecting\na request whose ",[171,778,746],{}," is not loopback, DNS rebinding) and a ",[166,781,782],{},"same-site\nguard"," that rejects state-changing requests a browser marks as cross-site.\nJSON request bodies are capped at 8 MiB.",[162,785,786,787,790,791,794,795,798,799,802,803,805,806,809,810,813,814,817,818,660,821,824,825,828,829,832,833,836,837,840,841,844],{},"Deploys and compose files that reach the worker's ",[166,788,789],{},"state\u002Fidentity directory,\nthe docker socket, or the host root"," are refused, on the worker and on the\nclient (so a local run and a remote run behave the same). The check is\nboundary-aware and covers ",[166,792,793],{},"ancestors"," of the state directory (e.g.\n",[171,796,797],{},"\u002Fhome\u002Fant"," exposes ",[171,800,801],{},"\u002Fhome\u002Fant\u002F.ant\u002Fworker\u002Fagent-identity","). A project-relative\nmount such as ",[171,804,568],{}," or ",[171,807,808],{},".\u002Fdata"," is allowed in a compose file (it is relative\nto the compose\u002Fproject directory, not the worker's home) while a\n",[171,811,812],{},"run: container"," deploy refuses relative bind sources outright, because the\nworker has no project directory to resolve them against. For compose this\ncovers ",[171,815,816],{},"volumes",", top-level ",[171,819,820],{},"secrets:",[171,822,823],{},"configs:"," ",[171,826,827],{},"file:"," sources, and it\nalso rejects ",[171,830,831],{},"privileged: true"," and host ",[171,834,835],{},"devices:",". A resolved compose\ndocument that still contains ",[171,838,839],{},"include:"," is refused on the worker: compose\nexpands it at ",[171,842,843],{},"up"," time, after the guards, so its services could bypass every\ncheck.",[162,846,847,848,851,852,855,856,859,860,863,864,867],{},"Untrusted build contexts are extracted with an ",[166,849,850],{},"8 GiB cap"," and reject\nabsolute paths, ",[171,853,854],{},".."," traversal, hard links, and symlinks that leave the\ncontext. The transport caps a request frame at ",[166,857,858],{},"2 MiB"," (a general frame at\n",[166,861,862],{},"8 MiB","), a blob at ",[166,865,866],{},"64 GiB",", and bounds connections and streams per\nconnection.",[162,869,870,871,874,875,878],{},"Every privileged RPC is written to a ",[166,872,873],{},"tamper-evident audit log","\n(",[171,876,877],{},"ant nest audit","): each entry chains to the previous by hash and each rotated\nsegment links to the one before it, so an edited entry or a deleted segment\nfails verification. (The newest lines can still be truncated by anyone who can\nwrite the state directory, and deleting the oldest retained segment is\nindistinguishable from normal rotation.) The log rotates at 8 MiB, and reading\nit is an admin action that does not itself append.",[162,880,881,882,885,886,889,890,893,894,897],{},"Secret values are never stored: a ",[171,883,884],{},"from:"," provider (1Password ",[171,887,888],{},"op:\u002F\u002F",", a\ngeneric ",[171,891,892],{},"cmd:",", or ",[171,895,896],{},"env:",") is resolved at deploy time and only its warning is\nsurfaced on failure.",[162,899,900,507,902,905,906,909,910,913,914,568],{},[166,901,105],{},[171,903,904],{},"ant nest tunnel",", or the dashboard's ",[166,907,908],{},"Tunnel"," action) forward\nbytes to a ",[166,911,912],{},"published loopback port of a container ant manages"," and nothing\nelse, so they add no reach beyond the deploy capability: a peer cannot use one\nto reach the docker socket, the Caddy admin API, or another local listener.\nSee ",[915,916,105],"a",{"href":106},[606,918,920],{"id":919},"protocol-version","Protocol version",[150,922,923,924,927,928,931],{},"Every RPC carries an API version, and both sides reject a mismatch with an\nactionable message (",[171,925,926],{},"peer speaks protocol vN, this worker speaks v1; upgrade the older side",") instead of failing as an unknown method. A mixed-version fleet\ntherefore fails closed on the first call, which is why upgrading worker and CLI\ntogether matters (see the upgrade note in Known issues). Before the first\ntagged release the protocol is not stable and the version stays ",[166,929,930],{},"1","; it is\nbumped only once there are deployed workers that cannot simply be rebuilt.",[154,933,935],{"id":934},"at-rest","At rest",[159,937,938,950],{},[162,939,940,943,944,946,947,949],{},[171,941,942],{},"~\u002F.ant\u002Fconfig.json",", the machine ",[171,945,491],{},", and identity files are ",[171,948,210],{},";\nthe machine state is written atomically.",[162,951,952,953,956,957,960,961,824,963,966,967,970,971,974,975,978,979,982],{},"Build-time secrets are resolved from the environment and never baked into\nimages or the lockfile. Runtime secrets never appear in command argv or a\npersisted compose document: a container deploy (local or remote) passes them\nthrough the docker process environment (a bare ",[171,954,955],{},"docker run -e KEY",", value\nfrom the invoking process's environment, so multi-line values survive), and a\ncompose deploy ships ",[171,958,959],{},"${KEY}"," references plus a ",[171,962,210],{},[171,964,965],{},"secrets.env"," beside\nthe compose file (",[171,968,969],{},"docker compose --env-file",", removed by ",[171,972,973],{},"compose down",").\nAnt stores ",[166,976,977],{},"no secret values of its own",": there is no secret store, no\nvault, and no encrypted field in the config or state files. Docker itself\nrecords a container's environment, so ",[171,980,981],{},"docker inspect"," still shows the values at\nruntime; that is the container engine's storage, not ant's.",[154,984,986],{"id":985},"trust-boundaries-and-gaps","Trust boundaries and gaps",[159,988,989,995,1006,1012,1018],{},[162,990,991,994],{},[166,992,993],{},"The local dashboard trusts loopback."," It performs real mutations (users,\ndeploy, config). A non-loopback bind requires a shared password and should sit\nbehind TLS; there is no per-user login or OIDC yet, so prefer a single-operator\nor otherwise trusted host.",[162,996,997,1000,1001,1003,1004,568],{},[166,998,999],{},"The audit log is local and root-deletable."," Privileged actions are\nhash-chained and ",[171,1002,877],{}," detects an edited or removed line, but the\nlog lives on the machine and is not shipped off-host; an operator with root can\ndelete it. See ",[915,1005,120],{"href":121},[162,1007,1008,1011],{},[166,1009,1010],{},"Owner election is a modeled quorum."," When the roster has zero owners and at\nleast two admins, either admin may promote any member (including itself) to\nowner through the daemon; the second admin's consent is asserted by the\ncaller, not verified, and the promotion is recorded in the audit log. Treat an\nadmin account as equivalent to a potential owner.",[162,1013,1014,1017],{},[166,1015,1016],{},"No trust-on-first-use"," for a machine's NodeID beyond the value entered;\ninvite tokens pin the machine NodeID, direct registration does not.",[162,1019,1020,1023,1024,1027,1028,1031],{},[166,1021,1022],{},"Supply chain",": the iroh transport uses an unofficial, vendored Go binding\n(",[171,1025,1026],{},"github.com\u002Ftheinventorylib\u002Firoh-go",") with a static library; cosign\nverification exists but is opt-in (",[171,1029,1030],{},"deploy.image_signing","), and both the local\nCLI and the worker verify the signature before running when it is set.",{"title":1033,"searchDepth":1034,"depth":1034,"links":1035},"",2,[1036,1037,1038,1039,1040,1046,1047],{"id":156,"depth":1034,"text":157},{"id":192,"depth":1034,"text":56},{"id":218,"depth":1034,"text":219},{"id":495,"depth":1034,"text":496},{"id":539,"depth":1034,"text":540,"children":1041},[1042,1044,1045],{"id":608,"depth":1043,"text":609},3,{"id":698,"depth":1043,"text":699},{"id":919,"depth":1043,"text":920},{"id":934,"depth":1034,"text":935},{"id":985,"depth":1034,"text":986},"md",null,{},{"icon":118},{"title":115,"description":1053},"Identity, mutual-TLS transport, per-RPC authorization, invites, least privilege, and the known gaps.","nCbdA_hG-omMJbqthN8in1C4xMH1JR32a0ZS-mPptnk",[1056,1058],{"title":105,"path":106,"stem":107,"description":1057,"icon":108,"children":-1},"Reach a managed container port from your machine: no inbound port on either side.",{"title":120,"path":121,"stem":122,"description":1059,"icon":123,"children":-1},"Deferred work, with why and the path forward.",1791494555934]