[{"data":1,"prerenderedAt":83},["ShallowReactive",2],{"search-en":3,"content-en-developer\u002Fdecisions\u002F052-connected-apps-and-signed-hooks":4,"surround-en-\u002Fdeveloper\u002Fdecisions\u002F052-connected-apps-and-signed-hooks":74},[],{"id":5,"title":6,"body":7,"description":66,"extension":67,"meta":68,"navigation":69,"path":70,"seo":71,"stem":72,"__hash__":73},"content_en\u002F3.developer\u002Fdecisions\u002F15.052-connected-apps-and-signed-hooks.md","ADR-052: Connected Apps and Signed Hooks",{"type":8,"value":9,"toc":59},"minimark",[10,27,32,36,40,43,46,50],[11,12,13,21],"ul",{},[14,15,16,20],"li",{},[17,18,19],"strong",{},"Status:"," Accepted",[14,22,23,26],{},[17,24,25],{},"Date:"," 2026-07-18",[28,29,31],"h2",{"id":30},"context","Context",[33,34,35],"p",{},"Some integrations must deploy independently of Owlat and cannot run inside Convex or the Nuxt bundle. Their network responses, credentials, and endpoints are untrusted and need a smaller boundary than arbitrary callbacks.",[28,37,39],{"id":38},"decision","Decision",[33,41,42],{},"A connected app is bound to one bundled plugin, explicit capabilities, and plugin-scoped API keys. Shared hook secrets are generated with a CSPRNG, sealed with AES-256-GCM under an HKDF-derived key, revealed once, and never returned by a query.",[33,44,45],{},"Synchronous draft, gate, and score hooks use domain-separated HMAC-SHA256 signatures over the exact body and request metadata. Nonces prevent replay; verification is constant-time. Endpoint validation and guarded fetches defend against private targets, DNS rebinding, and unsafe redirects. Disable or revoke is checked before every operation, and revocation cascades to plugin-bound API keys.",[28,47,49],{"id":48},"consequences","Consequences",[33,51,52,53,58],{},"Connected apps can extend Owlat without receiving a Convex context or deployment secrets. Network uncertainty fails in the safe direction, delivery logs are tenant-scoped and redacted, and restrict-only gate responses cannot approve a send. See ",[54,55,57],"a",{"href":56},"\u002Fdeveloper\u002Fplugin-connected-apps","Connected Apps",".",{"title":60,"searchDepth":61,"depth":61,"links":62},"",2,[63,64,65],{"id":30,"depth":61,"text":31},{"id":38,"depth":61,"text":39},{"id":48,"depth":61,"text":49},"Tier-2 integrations use plugin-bound credentials and replay-resistant, fail-closed synchronous hooks.","md",{},true,"\u002Fdeveloper\u002Fdecisions\u002F052-connected-apps-and-signed-hooks",{"title":6,"description":66},"3.developer\u002Fdecisions\u002F15.052-connected-apps-and-signed-hooks","YNaItZ00eC0ZrlLOWkGeJ8GGeGTVWm7RCqMy3hvM4Y4",[75,79],{"title":76,"path":77,"stem":78,"children":-1},"ADR-051: Ordered Restrict-Only Autonomy Gates","\u002Fdeveloper\u002Fdecisions\u002F051-ordered-autonomy-gates","3.developer\u002Fdecisions\u002F14.051-ordered-autonomy-gates",{"title":80,"path":81,"stem":82,"children":-1},"ADR-053: Sandboxed Plugin Jobs","\u002Fdeveloper\u002Fdecisions\u002F053-sandboxed-plugin-jobs","3.developer\u002Fdecisions\u002F16.053-sandboxed-plugin-jobs",1786915101595]