[{"data":1,"prerenderedAt":641},["ShallowReactive",2],{"navigation":3,"\u002Fsecurity\u002Fthreat-model":89,"\u002Fsecurity\u002Fthreat-model-surround":636},[4,40,63,76],{"title":5,"path":6,"stem":7,"children":8,"icon":39},"Getting Started","\u002Fgetting-started","1.getting-started\u002F1.index",[9,11,15,19,23,27,31,35],{"title":10,"path":6,"stem":7},"Introduction",{"title":12,"path":13,"stem":14},"Quick Start: Service Provider","\u002Fgetting-started\u002Fquickstart-sp","1.getting-started\u002F2.quickstart-sp",{"title":16,"path":17,"stem":18},"Quick Start: Identity Provider","\u002Fgetting-started\u002Fquickstart-idp","1.getting-started\u002F3.quickstart-idp",{"title":20,"path":21,"stem":22},"Quick Start: Agent","\u002Fgetting-started\u002Fquickstart-agent","1.getting-started\u002F4.quickstart-agent",{"title":24,"path":25,"stem":26},"Quick Start","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F5.installation",{"title":28,"path":29,"stem":30},"How It Works","\u002Fgetting-started\u002Fhow-it-works","1.getting-started\u002F6.how-it-works",{"title":32,"path":33,"stem":34},"For Service Providers","\u002Fgetting-started\u002Ffor-service-providers","1.getting-started\u002F7.for-service-providers",{"title":36,"path":37,"stem":38},"CLI (apes & ape-shell)","\u002Fgetting-started\u002Fcli","1.getting-started\u002F8.cli",false,{"title":41,"path":42,"stem":43,"children":44,"icon":39},"Ecosystem","\u002Fecosystem","2.ecosystem\u002F1.index",[45,47,51,55,59],{"title":46,"path":42,"stem":43},"Overview",{"title":48,"path":49,"stem":50},"OpenApe Auth","\u002Fecosystem\u002Fauth","2.ecosystem\u002F2.auth",{"title":52,"path":53,"stem":54},"OpenApe Grants","\u002Fecosystem\u002Fgrants","2.ecosystem\u002F3.grants",{"title":56,"path":57,"stem":58},"nuxt-auth-sp","\u002Fecosystem\u002Fnuxt-auth-sp","2.ecosystem\u002F4.nuxt-auth-sp",{"title":60,"path":61,"stem":62},"escapes","\u002Fecosystem\u002Fescapes","2.ecosystem\u002F5.escapes",{"title":64,"icon":39,"path":65,"stem":66,"children":67,"page":39},"Security","\u002Fsecurity","3.security",[68,72],{"title":69,"path":70,"stem":71},"Compliance","\u002Fsecurity\u002Fcompliance","3.security\u002F1.compliance",{"title":73,"path":74,"stem":75},"Threat Model","\u002Fsecurity\u002Fthreat-model","3.security\u002F2.threat-model",{"title":77,"icon":39,"path":78,"stem":79,"children":80,"page":39},"Guides","\u002Fguides","4.guides",[81,85],{"title":82,"path":83,"stem":84},"Capabilities & Grants","\u002Fguides\u002Fcapabilities","4.guides\u002F1.capabilities",{"title":86,"path":87,"stem":88},"Delegation","\u002Fguides\u002Fdelegation","4.guides\u002F2.delegation",{"id":90,"title":73,"body":91,"description":629,"extension":630,"links":631,"meta":632,"navigation":633,"path":74,"seo":634,"stem":75,"__hash__":635},"docs\u002F3.security\u002F2.threat-model.md",{"type":92,"value":93,"toc":608},"minimark",[94,98,103,107,119,123,129,134,140,154,157,161,164,169,196,200,215,218,222,229,236,257,261,268,271,275,278,314,321,329,343,347,351,357,367,370,384,388,399,407,417,421,424,428,439,444,447,473,484,489,500,505,520,524],[95,96,73],"h1",{"id":97},"threat-model",[99,100,102],"h2",{"id":101},"email-as-public-input","Email as Public Input",[104,105,106],"p",{},"The user's email is entered at the SP to trigger DNS discovery. This is inherently visible to the SP — an accepted, unclosable information surface.",[104,108,109,113,114,118],{},[110,111,112],"strong",{},"Why it's OK:"," The email is needed for DNS resolution (",[115,116,117],"code",{},"_ddisa.{domain}","). Passkeys ensure that knowing someone's email is insufficient to impersonate them. This is the same model as email itself — knowing an address doesn't give you access.",[99,120,122],{"id":121},"dns-discovery-trust-model","DNS Discovery Trust Model",[104,124,125,126,128],{},"DDISA uses DNS TXT records (",[115,127,117],{},") to discover which IdP is authoritative for a given email domain. This makes DNS a critical part of the trust chain. Here's how the protocol handles DNS-related threats.",[130,131,133],"h3",{"id":132},"dns-spoofing-redirect-to-malicious-idp","DNS Spoofing → Redirect to Malicious IdP",[104,135,136,139],{},[110,137,138],{},"Threat:"," An attacker controls the DNS response (cache poisoning, rogue resolver, local MITM) and points the user to a malicious IdP.",[104,141,142,145,146,149,150,153],{},[110,143,144],{},"Why passkeys neutralize this:"," WebAuthn passkeys are origin-bound. The authenticator signs challenges only for the origin where the credential was originally registered. If an attacker redirects a user to ",[115,147,148],{},"evil-idp.example"," instead of the legitimate ",[115,151,152],{},"idp.example",", the authenticator simply refuses to produce an assertion — the origins don't match. The attacker gets nothing.",[104,155,156],{},"This means DNS spoofing cannot be used to steal credentials or impersonate existing users.",[130,158,160],{"id":159},"enrollment-the-one-real-risk","Enrollment: The One Real Risk",[104,162,163],{},"During first-time enrollment, no passkey exists yet — there is no origin binding to protect the user. If DNS is spoofed at this exact moment, the user could register a passkey with a malicious IdP.",[104,165,166],{},[110,167,168],{},"Mitigations:",[170,171,172,184,190],"ul",{},[173,174,175,178,179,183],"li",{},[110,176,177],{},"Email verification:"," The IdP sends a verification email before completing enrollment. An attacker would need to control both DNS ",[180,181,182],"em",{},"and"," the user's email.",[173,185,186,189],{},[110,187,188],{},"Visual confirmation:"," The user sees the IdP URL in their browser. Unexpected domains are noticeable.",[173,191,192,195],{},[110,193,194],{},"DNSSEC:"," Domain operators should enable DNSSEC to cryptographically sign DNS responses (see below).",[130,197,199],{"id":198},"transport-security-doh","Transport Security (DoH)",[104,201,202,203,206,207,210,211,214],{},"In edge and browser environments, DNS resolution typically uses DNS-over-HTTPS (DoH) via providers like Cloudflare (",[115,204,205],{},"1.1.1.1","), Google (",[115,208,209],{},"8.8.8.8","), or Quad9 (",[115,212,213],{},"9.9.9.9","). The TLS connection to the DoH provider protects against local network-level MITM attacks on DNS queries.",[104,216,217],{},"In Node.js and Bun server environments, DNS resolution uses the system resolver. Security depends on the deployment infrastructure — data center DNS is typically more trustworthy than coffee shop Wi-Fi, but operators should be aware of this difference.",[130,219,221],{"id":220},"dnssec-recommended-not-required","DNSSEC: Recommended, Not Required",[104,223,224,225,228],{},"DNSSEC cryptographically signs DNS responses, protecting against cache poisoning and response tampering. Domain operators using DDISA ",[110,226,227],{},"should"," enable DNSSEC as a best practice.",[104,230,231,232,235],{},"The protocol does not ",[180,233,234],{},"require"," DNSSEC because:",[237,238,239,245,251],"ol",{},[173,240,241,244],{},[110,242,243],{},"Passkey origin binding"," already covers the critical authentication path (existing users cannot be phished via DNS spoofing)",[173,246,247,250],{},[110,248,249],{},"Enrollment"," is a one-time event with additional mitigations (email verification, visual confirmation)",[173,252,253,256],{},[110,254,255],{},"Forcing DNSSEC"," would exclude domains that haven't enabled it, reducing adoption without proportional security gain",[130,258,260],{"id":259},"fallback-behavior","Fallback Behavior",[104,262,263,264,267],{},"When no ",[115,265,266],{},"_ddisa."," TXT record exists for a domain, the SP falls back to a configured default IdP. This is by design — not every domain will publish DDISA records.",[104,269,270],{},"The SP operator controls the fallback behavior: which default IdP to use, or whether to reject domains without DDISA records entirely. This is a deployment decision, not a protocol-level security boundary.",[99,272,274],{"id":273},"minimal-authn-jwt","Minimal AuthN-JWT",[104,276,277],{},"The identity token contains only:",[170,279,280,286,299],{},[173,281,282,285],{},[115,283,284],{},"sub"," — email (consistent from input to token)",[173,287,288,291,292,295,296],{},[115,289,290],{},"act"," — ",[115,293,294],{},"human"," or ",[115,297,298],{},"agent",[173,300,301,304,305,304,308,304,311],{},[115,302,303],{},"iss",", ",[115,306,307],{},"aud",[115,309,310],{},"exp",[115,312,313],{},"nonce",[104,315,316,317,320],{},"Explicitly ",[110,318,319],{},"not"," in the AuthN-JWT:",[170,322,323,326],{},[173,324,325],{},"Name, display name → IdP-internal",[173,327,328],{},"Owner, approver → AuthZ layer (grants)",[104,330,331,334,335,338,339,342],{},[110,332,333],{},"Rationale:"," AuthN says ",[180,336,337],{},"who",". AuthZ says ",[180,340,341],{},"what may they do",". Mixing them increases token size and leaks information the SP doesn't need.",[99,344,346],{"id":345},"grant-security","Grant Security",[130,348,350],{"id":349},"command-hash-binding","Command Hash Binding",[104,352,353,354,356],{},"For ",[115,355,60],{}," and similar tools, grants are bound to the exact command via SHA-256:",[358,359,364],"pre",{"className":360,"code":362,"language":363},[361],"language-text","cmd_hash = sha256(\"apt-get upgrade\")\n","text",[115,365,362],{"__ignoreMap":366},"",[104,368,369],{},"The target system computes the hash locally and compares. If it doesn't match, execution is aborted. This prevents:",[170,371,372,378],{},[173,373,374,377],{},[110,375,376],{},"Substitution attacks"," — approving \"whoami\" but executing \"rm -rf \u002F\"",[173,379,380,383],{},[110,381,382],{},"Replay with different commands"," — the hash won't match",[130,385,387],{"id":386},"dual-accountability","Dual Accountability",[104,389,390,391,394,395,398],{},"Grants track both the ",[110,392,393],{},"agent's identity"," and the ",[110,396,397],{},"approver's identity"," separately. In many setups, the agent owner and the approver are different people:",[170,400,401,404],{},[173,402,403],{},"Agent owner: the person responsible for the agent",[173,405,406],{},"Approver: the person who approved this specific action",[104,408,409,410,412,413,416],{},"Both are recorded in the AuthZ-JWT (",[115,411,284],{}," + ",[115,414,415],{},"decided_by",").",[130,418,420],{"id":419},"default-deny","Default Deny",[104,422,423],{},"No grant = no access. Agents start with zero permissions and must request each one. There is no \"admin agent\" concept — every privileged action requires explicit human approval (or a standing grant).",[130,425,427],{"id":426},"management-token-separation","Management Token Separation",[104,429,430,431,434,435,438],{},"The IdP Management Token (",[115,432,433],{},"NUXT_OPENAPE_MANAGEMENT_TOKEN",") is an ",[110,436,437],{},"infrastructure credential"," — equivalent to a database root password or a cloud provider's admin key. It belongs exclusively to human administrators and must never be accessible to agents.",[104,440,441],{},[110,442,443],{},"Why this boundary is critical:",[104,445,446],{},"If an agent obtains the Management Token, it can bypass the entire grant system:",[237,448,449,455,461,467],{},[173,450,451,454],{},[110,452,453],{},"Self-registration"," — create new agent identities without human approval",[173,456,457,460],{},[110,458,459],{},"Grant self-approval"," — approve its own privilege requests",[173,462,463,466],{},[110,464,465],{},"Identity forgery"," — register arbitrary agents and issue tokens for them",[173,468,469,472],{},[110,470,471],{},"Audit destruction"," — the grant log becomes meaningless because the approver and requester are the same entity",[104,474,475,476,479,480,483],{},"This is not a theoretical risk — it is the single most important security boundary in an OpenApe deployment. The grant system's value depends entirely on the separation between ",[180,477,478],{},"who administers the IdP"," (humans with the Management Token) and ",[180,481,482],{},"who requests permissions through it"," (agents with Ed25519 keys).",[104,485,486],{},[110,487,488],{},"Correct deployment:",[170,490,491,494,497],{},[173,492,493],{},"Store the Management Token in a secrets manager or environment variable accessible only to the deployment infrastructure",[173,495,496],{},"Never write it into agent configuration files, shell histories, or automation scripts that agents can read",[173,498,499],{},"Use separate credentials for CI\u002FCD pipelines — if a pipeline needs IdP access, give it a scoped API key, not the Management Token",[104,501,502],{},[110,503,504],{},"Anti-patterns:",[170,506,507,510,517],{},[173,508,509],{},"Passing the Management Token as a CLI argument to agent processes",[173,511,512,513,516],{},"Storing it in a shared ",[115,514,515],{},".env"," file on a machine where agents run",[173,518,519],{},"Using the Management Token \"temporarily\" for agent enrollment (use the enrollment URL flow instead)",[99,521,523],{"id":522},"credential-overview","Credential Overview",[525,526,527,546],"table",{},[528,529,530],"thead",{},[531,532,533,537,540,543],"tr",{},[534,535,536],"th",{},"Credential",[534,538,539],{},"Who holds it",[534,541,542],{},"Purpose",[534,544,545],{},"Agent access?",[547,548,549,566,580,594],"tbody",{},[531,550,551,555,558,561],{},[552,553,554],"td",{},"Management Token",[552,556,557],{},"Human admin",[552,559,560],{},"IdP administration",[552,562,563],{},[110,564,565],{},"NEVER",[531,567,568,571,574,577],{},[552,569,570],{},"Ed25519 Private Key",[552,572,573],{},"User who operates the agent",[552,575,576],{},"Agent authentication",[552,578,579],{},"Yes (user-owned, generated during enrollment or provided)",[531,581,582,585,588,591],{},[552,583,584],{},"Passkey",[552,586,587],{},"Human user",[552,589,590],{},"Human authentication",[552,592,593],{},"Hardware-bound",[531,595,596,599,602,605],{},[552,597,598],{},"Session Secret",[552,600,601],{},"Deployment",[552,603,604],{},"Cookie signing",[552,606,607],{},"Infrastructure only",{"title":366,"searchDepth":609,"depth":610,"links":611},1,2,[612,613,621,622,628],{"id":101,"depth":610,"text":102},{"id":121,"depth":610,"text":122,"children":614},[615,617,618,619,620],{"id":132,"depth":616,"text":133},3,{"id":159,"depth":616,"text":160},{"id":198,"depth":616,"text":199},{"id":220,"depth":616,"text":221},{"id":259,"depth":616,"text":260},{"id":273,"depth":610,"text":274},{"id":345,"depth":610,"text":346,"children":623},[624,625,626,627],{"id":349,"depth":616,"text":350},{"id":386,"depth":616,"text":387},{"id":419,"depth":616,"text":420},{"id":426,"depth":616,"text":427},{"id":522,"depth":610,"text":523},"Security analysis and design decisions.","md",null,{},true,{"title":73,"description":629},"3FB54iLbV---lwVUw73ZGtzgw_gZlqUNBbJEwNoz3Fk",[637,639],{"title":69,"path":70,"stem":71,"description":638,"children":-1},"NIS2, NIST CSF 2.0, and regulatory compliance.",{"title":82,"path":83,"stem":84,"description":640,"children":-1},"How agents request permissions and humans approve them.",1776885317056]