[{"data":1,"prerenderedAt":884},["ShallowReactive",2],{"blog-en-today-the-agent-does-what-he-isnt-allowed-to":3,"header-blog-translations-/en/blog/today-the-agent-does-what-he-isnt-allowed-to":881},{"id":4,"title":5,"author":6,"body":7,"date":863,"description":864,"draft":865,"extension":866,"image":867,"meta":868,"navigation":869,"path":870,"seo":871,"stem":872,"tags":873,"translationKey":879,"__hash__":880},"blog_en/blog/en/today-the-agent-does-what-he-isnt-allowed-to.md","Today the Agent Does What He Isn't Allowed To","Patrick Hofmann",{"type":8,"value":9,"toc":851},"minimark",[10,14,26,29,34,53,56,65,75,93,104,108,115,152,155,159,166,176,182,197,201,208,211,221,236,350,353,361,367,372,447,468,471,475,484,490,500,504,511,635,638,642,649,664,671,675,682,688,694,698,704,707,713,716,722,729,751,760,766,771,774,780,786,793,797,804,807,809,816,819,847],[11,12,13],"p",{},"Today my agent is going to do something it isn't allowed to do.",[11,15,16,17,21,22,25],{},"This is not a metaphor. openclaw runs on a mini PC at home as its own OS user named ",[18,19,20],"code",{},"openclaw",". This user has no sudo entry. No password. Nothing in ",[18,23,24],{},"/etc/sudoers",". No NOPASSWD. This is deliberate — the whole point of running the agent as its own unprivileged user is that I never have to implicitly trust what it does.",[11,27,28],{},"Today it needs a command as root.",[30,31,33],"h2",{"id":32},"the-default-reflex","The Default Reflex",[11,35,36,37,41,42,45,46,49,50],{},"If you search the internet for ",[38,39,40],"em",{},"\"how to give an AI agent sudo,\""," pretty much every tutorial gives you the same suggestion: add the agent user to ",[18,43,44],{},"/etc/sudoers.d/",", set ",[18,47,48],{},"NOPASSWD: ALL",", and you're done. Sometimes the line is scoped to specific binaries, but the underlying attitude is the same: ",[38,51,52],{},"trusted once, trusted forever, no questions asked.",[11,54,55],{},"I've never done this and never will. The reasons aren't paranoia — they're architecture.",[11,57,58,64],{},[59,60,61,63],"strong",{},[18,62,48],{}," is not a security statement. It's the abandonment of security."," The sudo mechanism exists to require proof of authorization. When that proof is removed, the remaining effect of sudo is just switching the effective UID. The entire audit and policy layer is gone.",[11,66,67,70,71,74],{},[59,68,69],{},"The agent becomes a permanently privileged user."," Whoever gains access to the running agent process — a compromised npm package in a tool, a prompt injection in a remote document, a buggy hook — has root on the machine from that moment on. Not for an hour, not ",[38,72,73],{},"\"while the agent is actively working,\""," but structurally and permanently.",[11,76,77,88,89,92],{},[59,78,79,80,83,84,87],{},"Caching turns ",[38,81,82],{},"approved once"," into ",[38,85,86],{},"approved forever","."," Standard sudo has a ",[18,90,91],{},"timestamp_timeout"," of 5 to 15 minutes. For interactive humans, that's convenient. For an agent that can theoretically issue commands every second, it means: a single approved command is enough, and the entire window afterward is wide open.",[11,94,95,96,99,100,103],{},"Together this means: ",[18,97,98],{},"NOPASSWD"," is not a ",[38,101,102],{},"somewhat weaker"," variant of proper sudo. It's categorically something else. It gives up the user as a security boundary.",[30,105,107],{"id":106},"what-sudo-assumes-and-why-those-assumptions-break-for-agents","What sudo Assumes, and Why Those Assumptions Break for Agents",[11,109,110,111,114],{},"sudo grew out of a world where ",[38,112,113],{},"the user"," sits at the terminal and types. Three assumptions are baked into this design:",[116,117,118,133,143],"ul",{},[119,120,121,124,125,128,129,132],"li",{},[59,122,123],{},"The invoker is the authorized person."," The password is the bridge between ",[38,126,127],{},"body at keyboard"," and ",[38,130,131],{},"entry in /etc/passwd",". When the invoker is a process without memory, no such bridge exists. A process can hold a password, but holding is not authentication — it's just storage.",[119,134,135,138,139,142],{},[59,136,137],{},"Cache optimization is good."," True for interactive humans who issue multiple commands during an admin session and don't want to re-enter their password each time. Catastrophic for agents, for whom ",[38,140,141],{},"\"multiple commands in a row\""," is the default case and exactly the situation that shouldn't be blanket-approved.",[119,144,145,148,149,151],{},[59,146,147],{},"Policy is decidable at configuration time."," True for admin workflows with a known role matrix. Not true for agent workflows, which are by definition unpredictable — nobody knows at 14:32 which command will be needed at 14:37, so nobody can write it into ",[18,150,44],{}," at 14:00.",[11,153,154],{},"These three assumptions aren't wrong — they're built for a different world. The world of humans. When you apply them unchanged to agents, you implicitly adopt the trust model of the human world, without the feedback loops that make it viable in the human world (social pressure, personal auditability, memory).",[30,156,158],{"id":157},"the-inversion","The Inversion",[11,160,161,162,165],{},"So I needed a mechanism that answers the question ",[38,163,164],{},"\"is this process allowed to run this one command as root right now?\""," live — not in advance, not cached, but once per command, and not by the agent itself, but by a human.",[11,167,168,169,172,173],{},"The thing I built for this is called ",[18,170,171],{},"escapes",". It's a setuid-root binary, written in Rust, publicly available on GitHub, and has exactly one job: ",[38,174,175],{},"execute a command with elevated privileges, but only if a signed grant token from an OpenApe IdP exists that was approved by a human in real time, for exactly this command, on exactly this machine, exactly once.",[177,178,179],"blockquote",{},[11,180,181],{},"The hard boundary stays hard. Only the crossing is audited.",[11,183,184,185,188,189,192,193,196],{},"The agent user gets ",[59,186,187],{},"nothing"," added. It remains unprivileged. It still has no entry in ",[18,190,191],{},"sudoers",". What it gets is the ability to ",[38,194,195],{},"request"," a grant — and if I as the approver agree, then for exactly a fraction of time, for exactly one command, the boundary is open. It's not the user that gets approved, but the crossing.",[30,198,200],{"id":199},"the-concrete-flow","The Concrete Flow",[11,202,203,204,207],{},"Here's what happens when openclaw wants to run ",[18,205,206],{},"whoami"," as root today.",[11,209,210],{},"The agent calls:",[212,213,218],"pre",{"className":214,"code":216,"language":217},[215],"language-text","apes run --as root -- whoami\n","text",[18,219,216],{"__ignoreMap":220},"",[11,222,223,224,227,228,231,232,235],{},"A single command. No ",[18,225,226],{},"sudo",". No password prompt. No existing session. The CLI ",[18,229,230],{},"apes"," sees the ",[18,233,234],{},"--as root"," flag and switches to the escapes audience flow. It creates a grant request at the IdP with a payload like this:",[212,237,241],{"className":238,"code":239,"language":240,"meta":220,"style":220},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"audience\": \"escapes\",\n  \"target_host\": \"mini.local\",\n  \"command\": [\"whoami\"],\n  \"decided_by\": \"patrick@hofmann.eco\"\n}\n","json",[18,242,243,252,279,300,324,344],{"__ignoreMap":220},[244,245,248],"span",{"class":246,"line":247},"line",1,[244,249,251],{"class":250},"sMK4o","{\n",[244,253,255,258,262,265,268,271,274,276],{"class":246,"line":254},2,[244,256,257],{"class":250},"  \"",[244,259,261],{"class":260},"spNyl","audience",[244,263,264],{"class":250},"\"",[244,266,267],{"class":250},":",[244,269,270],{"class":250}," \"",[244,272,171],{"class":273},"sfazB",[244,275,264],{"class":250},[244,277,278],{"class":250},",\n",[244,280,282,284,287,289,291,293,296,298],{"class":246,"line":281},3,[244,283,257],{"class":250},[244,285,286],{"class":260},"target_host",[244,288,264],{"class":250},[244,290,267],{"class":250},[244,292,270],{"class":250},[244,294,295],{"class":273},"mini.local",[244,297,264],{"class":250},[244,299,278],{"class":250},[244,301,303,305,308,310,312,315,317,319,321],{"class":246,"line":302},4,[244,304,257],{"class":250},[244,306,307],{"class":260},"command",[244,309,264],{"class":250},[244,311,267],{"class":250},[244,313,314],{"class":250}," [",[244,316,264],{"class":250},[244,318,206],{"class":273},[244,320,264],{"class":250},[244,322,323],{"class":250},"],\n",[244,325,327,329,332,334,336,338,341],{"class":246,"line":326},5,[244,328,257],{"class":250},[244,330,331],{"class":260},"decided_by",[244,333,264],{"class":250},[244,335,267],{"class":250},[244,337,270],{"class":250},[244,339,340],{"class":273},"patrick@hofmann.eco",[244,342,343],{"class":250},"\"\n",[244,345,347],{"class":246,"line":346},6,[244,348,349],{"class":250},"}\n",[11,351,352],{},"The IdP sends this request to me for approval. In the browser UI I see the full command, the target host, the agent, and the Approve/Deny buttons. I decide.",[11,354,355,360],{},[356,357],"img",{"alt":358,"src":359},"Browser approval screen: Permission Request showing Command whoami, Target MinivonPatrick.fritz.box, Run as root, Approval Type Once — Approve and Deny buttons","https://sos-at-vie-2.exo.io/dm-public/blog/2026-04-21/escapes-approval-screen.png"," When I approve, my passkey signs the grant, the IdP returns a JWT, the CLI takes the JWT and calls:",[212,362,365],{"className":363,"code":364,"language":217},[215],"escapes --grant \u003Cjwt> -- whoami\n",[18,366,364],{"__ignoreMap":220},[11,368,369,371],{},[18,370,171],{}," runs with effective UID 0 (setuid bit), verifying seven properties of the grant before it even considers executing anything:",[373,374,375,385,391,403,416,429,438],"ol",{},[119,376,377,380,381,384],{},[59,378,379],{},"Issuer"," is in ",[18,382,383],{},"allowed_issuers"," — only JWKS from these IdPs are fetched",[119,386,387,390],{},[59,388,389],{},"JWT signature"," is valid against the JWKS",[119,392,393,380,396,399,400,402],{},[59,394,395],{},"Approver",[18,397,398],{},"allowed_approvers"," (this is the equivalent of ",[18,401,191],{}," — but for humans, not processes)",[119,404,405,380,408,411,412,415],{},[59,406,407],{},"Audience",[18,409,410],{},"allowed_audiences"," (default: ",[18,413,414],{},"[\"escapes\"]",")",[119,417,418,422,423,425,426],{},[59,419,420],{},[18,421,286],{}," matches this machine's actual hostname — a grant for ",[18,424,295],{}," won't work on ",[18,427,428],{},"server01",[119,430,431,437],{},[59,432,433,434],{},"Command / ",[18,435,436],{},"cmd_hash"," matches exactly the command being passed",[119,439,440,446],{},[59,441,442,443],{},"IdP ",[18,444,445],{},"/consume"," confirms: this grant token has never been redeemed — replay protection",[11,448,449,450,452,453,456,457,460,461,464,465,87],{},"Only when all seven checks pass does ",[18,451,171],{}," sanitize the environment (strip ",[18,454,455],{},"LD_PRELOAD",", reset ",[18,458,459],{},"PATH"," to defaults, etc.) and call ",[18,462,463],{},"execvp(\"whoami\", [])",". The command runs as root, exactly once, sees exactly the argv I approved, on the machine I approved it for, and writes a full audit log entry to ",[18,466,467],{},"/var/log/openape/audit.log",[11,469,470],{},"After exit, it's over. The grant is consumed. If the agent needs another root command two minutes later, the whole dance starts over. No cache. No timestamp. No residual trust.",[30,472,474],{"id":473},"sudoers-stayed-empty","sudoers Stayed Empty",[11,476,477,478,480,481,483],{},"While writing this series, I checked the ",[18,479,226],{}," history to see when and why I've used sudo manually on my mini PC in the past few weeks. And to verify that the ",[18,482,20],{}," user actually passes through every line clean.",[11,485,486],{},[356,487],{"alt":488,"src":489},"Terminal screenshot: checking /etc/sudoers and /etc/sudoers.d/ — the openclaw user appears nowhere; at the same time two recently executed escapes commands with different grant IDs are visible, both regularly approved and audited","https://sos-at-vie-2.exo.io/dm-public/blog/TBD-escapes/sudoers-two-grants.png",[11,491,492,493,495,496,499],{},"This is the point that matters. The ",[18,494,191],{}," configuration of this machine has ",[59,497,498],{},"not changed"," through the entire process. No new entry. No NOPASSWD. No privileged user. What changed is that there's a second path — not through sudo, but beside sudo — where commands can be elevated per grant instead of per password. sudo stays what it was meant for: interactive humans sitting at a terminal and typing. escapes handles the agent case that sudo never modeled.",[30,501,503],{"id":502},"the-categorical-difference","The Categorical Difference",[11,505,506,507,510],{},"The question that came up under my ape-shell post: ",[38,508,509],{},"\"Can't you do this more simply? Sudoers with command whitelisting?\""," The answer is no, and the difference is not gradual. It's structural:",[512,513,514,529],"table",{},[515,516,517],"thead",{},[518,519,520,524,527],"tr",{},[521,522,523],"th",{},"Axis",[521,525,526],{},"sudo (with NOPASSWD)",[521,528,171],{},[530,531,532,548,561,577,592,608],"tbody",{},[518,533,534,540,545],{},[535,536,537],"td",{},[59,538,539],{},"When is policy decided?",[535,541,542,543],{},"At configuration time, static in ",[18,544,24],{},[535,546,547],{},"At runtime, per command, fresh",[518,549,550,555,558],{},[535,551,552],{},[59,553,554],{},"Who decides?",[535,556,557],{},"The invoker — i.e. the agent itself",[535,559,560],{},"A separate approver, decoupled from the invoker",[518,562,563,568,571],{},[535,564,565],{},[59,566,567],{},"Credential lifetime",[535,569,570],{},"Cache, 5-15 minute default",[535,572,573,574,576],{},"Single-use JWT, ",[18,575,445],{}," prevents replay",[518,578,579,584,587],{},[535,580,581],{},[59,582,583],{},"Command binding",[535,585,586],{},"Path-prefix matching (notoriously leaky)",[535,588,589,591],{},[18,590,436],{}," in the signed JWT",[518,593,594,599,605],{},[535,595,596],{},[59,597,598],{},"Host binding",[535,600,601,602],{},"Static in ",[18,603,604],{},"Host_Alias",[535,606,607],{},"Cryptographically anchored in the JWT",[518,609,610,615,618],{},[535,611,612],{},[59,613,614],{},"Audit",[535,616,617],{},"Local, often not aggregated",[535,619,620,621,624,625,624,628,624,630,624,633],{},"JSONL with ",[18,622,623],{},"grant_id",", ",[18,626,627],{},"approver",[18,629,436],{},[18,631,632],{},"issuer",[18,634,286],{},[11,636,637],{},"Every single row is a trust delegation point that sudo shifts to configuration time and escapes shifts to runtime. This shift is the actual content. Everything else — the Rust binary, the JWT, the seven checks — are the mechanisms that implement the shift technically.",[30,639,641],{"id":640},"humans-and-agents-are-equal-at-the-protocol-level","Humans and Agents Are Equal at the Protocol Level",[11,643,644,645,648],{},"A side effect that only became clear to me while building: I now use ",[18,646,647],{},"apes run --as root --"," for myself too. When I need a privileged command on one of the hosts my team manages, I type the same command my agent would type. Same flow. Same grant request. Same approval step.",[11,650,651,652,655,656,659,660,663],{},"The only difference: when ",[38,653,654],{},"I"," initiate the command, the approver is a team colleague. When the ",[38,657,658],{},"agent"," initiates it, I'm the approver. Same infrastructure, different role. This isn't coincidental. It's the principle ",[38,661,662],{},"humans and agents are equal at the protocol level",", from which the entire OpenApe story originates, concretely applied to the privilege layer.",[11,665,666,667,670],{},"The consequence is that escapes is not an agent-specific tool. It's general infrastructure that happens to also be usable by agents. The same equal treatment that OpenApe put front and center for the login flow (",[38,668,669],{},"\"the human has a session, the agent has a session — the infrastructure doesn't know which is which\"",") repeats here at the elevation flow.",[30,672,674],{"id":673},"what-this-costs","What This Costs",[11,676,677,678,681],{},"Working within the grant system means waiting for humans. If openclaw decides at 3 AM that it needs a privileged command, it waits. It doesn't wake me up. It doesn't proceed without me. This isn't specific to escapes — it applies to every grant in the entire OpenApe stack, whenever the agent operates at the edge of what it's allowed to do. ",[38,679,680],{},"Ask first"," is the whole point. But it's friction, and if you don't want this friction, you're in the wrong place.",[11,683,684,685,687],{},"It needs infrastructure. Without a running OpenApe-compatible Identity Provider, nothing works — someone has to hold the signing keys, publish the JWKS, run ",[18,686,445],{}," for replay protection. No IdP, no escapes.",[11,689,690,691,693],{},"And escapes is vibe-coded software. The security concept behind it is what matters — not whether my Rust code is bug-free. ",[18,692,226],{}," has over 40 years of open-source audit time behind it. escapes probably has bugs, and Linus Torvalds would probably agree with me on that. The code is small, MIT-licensed, and on GitHub — if you want to use it in production, look at it first.",[30,695,697],{"id":696},"getting-started","Getting Started",[212,699,702],{"className":700,"code":701,"language":217},[215],"cargo install openape-escapes\n",[18,703,701],{"__ignoreMap":220},[11,705,706],{},"Then grant the binary privileges — either via Linux capabilities:",[212,708,711],{"className":709,"code":710,"language":217},[215],"sudo setcap cap_setuid+ep $(which escapes)\n",[18,712,710],{"__ignoreMap":220},[11,714,715],{},"Or classically via the setuid bit:",[212,717,720],{"className":718,"code":719,"language":217},[215],"sudo chown root:root $(which escapes) && sudo chmod u+s $(which escapes)\n",[18,721,719],{"__ignoreMap":220},[11,723,724,725,728],{},"Then set up the trust relationship — ",[18,726,727],{},"/etc/openape/config.toml"," defines which IdP escapes trusts and who can approve grants:",[212,730,734],{"className":731,"code":732,"language":733,"meta":220,"style":220},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[security]\nallowed_issuers = [\"https://id.openape.at\"]\nallowed_approvers = [\"patrick@hofmann.eco\"]\n","toml",[18,735,736,741,746],{"__ignoreMap":220},[244,737,738],{"class":246,"line":247},[244,739,740],{},"[security]\n",[244,742,743],{"class":246,"line":254},[244,744,745],{},"allowed_issuers = [\"https://id.openape.at\"]\n",[244,747,748],{"class":246,"line":281},[244,749,750],{},"allowed_approvers = [\"patrick@hofmann.eco\"]\n",[11,752,753,754,756,757,759],{},"Two lines. ",[18,755,383],{}," is the list of IdPs whose JWKS are accepted. ",[18,758,398],{}," is the equivalent of sudoers — but for humans, not processes. Everything else has sensible defaults.",[11,761,762,763,765],{},"Then install ",[18,764,230],{},", configure an IdP, and run your first command:",[212,767,769],{"className":768,"code":216,"language":217},[215],[18,770,216],{"__ignoreMap":220},[11,772,773],{},"If everything is set up correctly, you'll get an approval request in the browser, approve it, and see:",[212,775,778],{"className":776,"code":777,"language":217},[215],"root\n",[18,779,777],{"__ignoreMap":220},[11,781,782],{},[356,783],{"alt":784,"src":785},"Telegram chat: the agent runs whoami as root — a grant is requested, after approval the answer comes back: root","https://sos-at-vie-2.exo.io/dm-public/blog/2026-04-21/escapes-whoami-root.png",[11,787,788,789,792],{},"That's it. One word. And behind it stand seven verification steps, a signed JWT, an audit log entry, and an explicitly consenting human. The same output as ",[18,790,791],{},"sudo whoami",", but a fundamentally different trust model.",[30,794,796],{"id":795},"why-this-is-the-right-pattern-for-me","Why This Is the Right Pattern for Me",[11,798,799,800,803],{},"Over the past few weeks I've been publicly building multiple layers of OpenApe — Identity, ape-shell, Claude Grant Gate, now escapes. Every single layer is a variation on the same thesis: ",[38,801,802],{},"Infrastructure over Instructions",". The agent isn't asked to follow rules. The environment makes rule violations structurally impossible.",[11,805,806],{},"escapes is the layer where this becomes most visible, because it hurts the most when you get it wrong. Giving an agent root means you've effectively given up the machine. Giving an agent a single-use grant means you've given up just that one operation. The difference is everything.",[11,808,181],{},[11,810,811,812,815],{},"And yes — ",[18,813,814],{},"cat /etc/shadow"," works too. Audit. Denied.",[817,818],"hr",{},[11,820,821],{},[38,822,823,826,827,834,835,840,841,846],{},[18,824,825],{},"openape-escapes@0.4.0"," is available on ",[828,829,833],"a",{"href":830,"rel":831},"https://crates.io/crates/openape-escapes",[832],"nofollow","crates.io"," and on ",[828,836,839],{"href":837,"rel":838},"https://github.com/openape-ai/escapes",[832],"GitHub",", MIT-licensed. The ",[828,842,845],{"href":843,"rel":844},"https://www.delta-mind.at/en/blog",[832],"previous articles in this series"," tell how OpenApe, ape-shell, and the grant integration behind them came about.",[848,849,850],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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":220,"searchDepth":254,"depth":254,"links":852},[853,854,855,856,857,858,859,860,861,862],{"id":32,"depth":254,"text":33},{"id":106,"depth":254,"text":107},{"id":157,"depth":254,"text":158},{"id":199,"depth":254,"text":200},{"id":473,"depth":254,"text":474},{"id":502,"depth":254,"text":503},{"id":640,"depth":254,"text":641},{"id":673,"depth":254,"text":674},{"id":696,"depth":254,"text":697},{"id":795,"depth":254,"text":796},"2026-04-17","My agent wants to run a command as root. It has no sudo entry, no password, nothing in /etc/sudoers. That's by design. Here's the path I built so it can execute exactly one command — approved by a human, audited, not cacheable, not reusable.",false,"md",null,{},true,"/blog/en/today-the-agent-does-what-he-isnt-allowed-to",{"title":5,"description":864},"blog/en/today-the-agent-does-what-he-isnt-allowed-to",[874,171,875,876,877,878],"OpenApe","AI Agents","Infrastructure","Security","Building in Public","todays-agent-forbidden-action","IlovI-S7eWLeyOIHdzB0tq7bvUohKb6sVPsQUCmTHSk",{"en":882,"de":883},"/en/blog/today-the-agent-does-what-he-isnt-allowed-to","/de/blog/heute-soll-der-agent-tun-was-er-nicht-darf",1776970806601]