# Limit a role to some objects

Pin the repositories ghapp reaches, then give each role the repositories it covers, so an agent with write tools writes only where one of its roles allows.

- Who: You, as the admin of the server, and an agent that drafts the change
- Where: A terminal with strazactl, the console, your midPoint and the agent's machine
- Profile: Standalone and enterprise

In short: On ghapp, a role with `pr_create` could open pull requests in every repository the App reaches. You pin ghapp's repositories in its manifest, store a listing credential so Straza lists them, and give each role the repositories it covers. The gateway then refuses a call whose `repo` no role of the caller covers.


AroleonanMCPservernamesthetoolsitsholdersmaycall.Onghapp,aGitHubAppserverthatreacheseveryrepositoryitsAppisinstalledon,arolewith`pr_create`couldthenopenpullrequestsinallofthem.Thispagepinsghapp'srepositoriesinStrazaandgiveseachroletherepositoriesitcovers,soanagentthatholdsawriterolewritesonlywherethatroleallows.Youendwitharolethatwritesintworepositories,arolethatreadsineveryrepository,anagentinmidPointthatholdsboth,andarefusedcallonarepositorythewriteroledoesnotcover.Youwritenodeclarationbyhand,becauseghappmarksitsowntools.Eachofits14repositorytools,suchas`repo_get`,`pr_create`and`pr_merge`,namesitsrepositoryinonerequiredargument,`repo`,asowner/name.Withgittransporton,`git_fetch`and`git_push`nametheirsthesameway.Itslistingtool,`repo_list`,returnseveryrepositorytheAppreacheswithGitHub'srepositoryidandthecustompropertiesyouaskfor,here`team`.Thattoolreacheseveryrepositoryatonce,soonlyaroleoneveryrepositorymayholdit,andghapphasnotoolthattouchesnorepository.##Beforeyoustart{.nostep}-ghapprunningwherestrazadcanreachit,startedwith`--http`onthataddress,withitsAppidandprivatekey.TheApponthispageisinstalledontheorganization`ghapp-straza-test`,whoserepositoriescarrythecustomproperty`team`.Startghappwith`GHAPP_MCP_ATTRIBUTES=team`,soitslistingreportsthatproperty.-Tworandomvaluesofyourownforghapp.`GHAPP_MCP_GATEWAY_SECRET`istheserver'sownsecret,whichStrazasendsoneverytoolcall.`GHAPP_MCP_LISTING_TOKEN`opens`repo_list`alone,andStrazaliststherepositorieswithit.-AStrazaglobaladmintoaddghapp.Intheconsole,fromthepintotheroles,ghapp'sownadminmaydoeveryStrazastep.Forthestrazactlsteps,anadminAPItokenin`STRAZA_API_TOKEN`thatholds`apps:read`,`apps:write`,`drafts:read`and`drafts:write`,mintedintheconsoleas[MintanadminAPItoken](https://docs.straza.ai/guides/console/settings/index.md#mint-an-admin-api-token)shows.Readingtheauditlogneedsaglobaladminoranadminrolewhoseareasinclude`audit:read`,as[Delegatedadmin](https://docs.straza.ai/guides/operate/delegated-admin/index.md)setsup.-midPointconnectedtoStrazaas[midPoint](https://docs.straza.ai/guides/connect-identity/midpoint/index.md)setsitup,anadminAPItokenwiththescope`scim:read`,andamidPointadminloginfortheassignment.AnySCIMclientreadsthesame`covers`values.-AnAIagentthatcallsthroughStraza'sgateway,suchas`joe-java-developer-agent`ofthedemostack.##Letanagentdraftthechange{.nostep}Anagentcanwritebothdraftsthispageneeds,andyoupublishthem.AcodingagentsuchasClaudeCodewiththeStrazaskillholdsnoStrazaadmincredential,soyourunthestrazactlreadsitasksforandhandittheiroutput.Itproposesadraftthroughthetool`straza__draft_submit`,whichthegatewaylistsonlytoasessionthatholdstherole`straza-draft-config`,as[Proposeandpublishachange](https://docs.straza.ai/guides/changes/propose-and-publish/index.md#let-an-agent-propose-a-change)shows.Noagentcanpublish,andthelistingcredentialneverpassesthroughone,becauseyoutypeitatahiddenprompt.Telltheagentwhichrepositorieseachteamworksin.Itthenworksinthisorder:1.Itaddstheblockthat`strazactlappsobjectsghapp`printstothemanifestthat`strazactlappsexportghapp`prints,fromtheoutputyouhandit,andsubmitsthefilethrough`straza__draft_submit`asthefirstdraft.`straza__draft_status`readstheserver'scheck.2.Youpublishthefirstdraftandstorethelistingcredential,asthestepsbelowshow.Strazathenlistsghapp'srepositories.3.Itwritesonerolefileperteamfromthelistthat`strazactlappsobjectsghapp`nowprints,withtherepositoriesyounamed,andsubmitsthefilesthrough`straza__draft_submit`astheseconddraft.4.YoupublishtheseconddraftandgivetherolestoagentsinyourIGAtool.TwodraftsareneededbecausearolecannamearepositoryonlyonceStrazahaslistedit.Aroleoneveryrepositoryneedsnolist,soitmayrideinthefirstdraft.Anagent'sdraftwaitsunder**Drafts**likeanyother,andyoupublishitintheconsole.Thestepsbelowmakethesamechangebyhand.Runtheircommandsinyourownterminal,onatokenthatnoagentcanread,neverinaterminalanagentshares.##Addghappghappisaremoteserverthatreadsabearertoken.Strazasendsghapp's`GHAPP_MCP_GATEWAY_SECRET`astheserver'sownsecretoneverytoolcall.Theaddress`http://ghapp:3003/mcp`isghapp'snameandportonthedemostack'sComposenetwork,soputtheaddressstrazadreachesghappatinitsplace.Intheconsole:Clickpathintheconsole:"MCPservers"›"AddMCPserver"OnServer,type`ghapp`,pick**Aserveryourun,overHTTP**andenterghapp'saddress.TheCredentialstepcomesnext:pick**Onesharedsecretforthisserver**,typethevalueof`GHAPP_MCP_GATEWAY_SECRET`,andleavethewayitissenton`AbearertokeninAuthorization`.Press**Saveandpublish**onReview.[Addaserver](https://docs.straza.ai/guides/serve-mcp-apps/add-a-server/index.md)walkseachstepofthewizard.WiththeCLI:Yourterminal,administration:```shcat>ghapp.yaml<<'EOF'apiVersion:straza.dev/v1beta1kind:Appmetadata:name:ghappnamespace:ai.strazadescription:GitHubthroughoneGitHubApp,atokenpercallforonereposerver:name:ai.straza/ghappdescription:Repos,pullrequestsandissuesonGitHub.EachcallgetsaGitHubtokenfortheonerepoitnames.repository:url:https://github.com/strazahq/ghapp-mcp-serversource:githubversion:"0.0.1"straza:runtime:kind:remoteremote:url:http://ghapp:3003/mcpcredential:kind:staticinject:as:headername:Authorizationtemplate:"Bearer{{secret}}"exposure:tools:["*"]limits:rps:10EOFstrazactlspecvalidateapp-fghapp.yamlstrazactlappsinstall-fghapp.yamlstrazactlappssecretsetghapp``````textghapp.yaml:appOKinstalledghapp(remoteruntime).Health:degraded,requiresacredentialandnoneisstored.Setonewithstrazactlappssecretsetghapp,whichasksforthevalueatahiddenprompt.secretsetfortheMCPserverghapp:theserver'sownsecret,fingerprintae0f```Theinstallsaysthatghappneedsitssecret,and`strazactlappssecretsetghapp`asksforitatahiddenprompt.Typethevalueof`GHAPP_MCP_GATEWAY_SECRET`there.##Pinghapp'srepositoriesPinningcopiesghapp'smarksintoitsmanifest,andStrazadecideswiththatcopyonly.Amarkthatghappchangeslaterthereforewaitsforapersontopublishit.WiththeCLI:Yourterminal,administration:```shstrazactlappsobjectsghappstrazactlappsobjectsdraftghapp``````textghapppinsnoobjecttype.Itstoolmarksdeclarethisblock:straza:objects:type:repolist:tool:repo_listattributes:-name:teamsetBy:orgtools:branch_list:argument:repofile_get:argument:repoissue_comment:argument:repoissue_create:argument:repoissue_get:argument:repoissue_list:argument:repopr_comment:argument:repopr_create:argument:repopr_get:argument:repopr_list:argument:repopr_merge:argument:reporepo_get:argument:reporepo_list:allsearch_code:argument:reposearch_issues:argument:repoPinitwithstrazactlappsobjectsdraftghapp,thenpublishthedraft.Createddraft39with1document.Nothingchangesuntilapersonpublishesit.~App/ghappwarningApp/ghappghapphasnolistingcredential,soStrazacannotlistitsrepos,andonlyrolesoneveryrepocanuseitsrepotoolsuntilithasone.Addonewithstrazactlappssecretsetghapp--listingafteryoupublish.Publishdraft39intheconsoleunderDrafts:http://localhost:8420/console/drafts/39```Onaserverthatpinsnothing,thefirstcommandprintsthe`straza.objects`blockthatghapp'smarksdeclareandendswiththecommandthatpinsit.Thesecondopensadraftofghapp'smanifestwiththatblock,noted`Pinsthereposthatghappdeclaresinitstoolmarks.`strazactlrunsonanadminAPItoken,andatokenneverpublishes,soapersonpublishesthedraftintheconsoleunder**Drafts**,astheconsoleformofthisstepshows.Intheconsole:Theconsolehasnobuttonthatpinsaserver,sothedraftcomesfrom`strazactlappsobjectsdraftghapp`orfromanagent.Youreviewandpublishithere:Clickpathintheconsole:"Drafts"›"Waiting"Selectthedraft.Under**Whatchanges**,ghapp'scardshowstherow`Pinnedtype`,whichreads`none`nowand`repo(repos),listedbyrepo_list.14toolsnamearepo,1reacheseveryrepo.`afterpublishing.Press**Publish…**,then**Publish**.Thepinnedblockreadsasfollows,writtenasyouwouldkeepitinamanifestfile,suchasonein[theappsdirectory](https://docs.straza.ai/guides/serve-mcp-apps/gitops-apps-directory/index.md).Eachrepositorytoolnames`repo`,`repo_list`reacheseveryrepository,and`list.attributes`keepsthe`team`propertythatghappreports.Theplural`repos`,thewordtheconsoleusesforthetabandthequestions,isthetypepluss,sotheblockleavesitout.```yamlstraza:objects:type:repolist:tool:repo_listattributes:-{name:team,setBy:org}tools:branch_list:{argument:repo}file_get:{argument:repo}issue_comment:{argument:repo}issue_create:{argument:repo}issue_get:{argument:repo}issue_list:{argument:repo}pr_comment:{argument:repo}pr_create:{argument:repo}pr_get:{argument:repo}pr_list:{argument:repo}pr_merge:{argument:repo}repo_get:{argument:repo}repo_list:allsearch_code:{argument:repo}search_issues:{argument:repo}```ghappreports`setBy:org`for`team`whenonlytheorganization'sownersmaysettheproperty,and`object`otherwise,includingwhenitcannotreadtheproperty'ssettings.Without`GHAPP_MCP_ATTRIBUTES`,theblockhasno`attributes`line.TheattributeshowsasacolumnontheRepostabandinthestrazactltable.Arolestillcoversrepositoriesbynameorcoverseveryone,andtheroleeditor'sfiltermatchesarepository'snameandtitleonly.Thedraft'scheckcomparestheblockwithghapp'slivetools.`repo_list`mustbemarkedread-only,and`repo`mustbearequiredstringargumentofeverytoolthatnamesit.Becausetheblocknameseverytool,nothingneedsadecision.Thecheckwarns`ghapphasnolistingcredential,soStrazacannotlistitsrepos,andonlyrolesoneveryrepocanuseitsrepotoolsuntilithasone.Addonewithstrazactlappssecretsetghapp--listingafteryoupublish.`Thewarningdoesnotstopthepublish,andthenextstepclearsit.Whentheserveralreadyhasroles,thefirstpingiveseachofthem`covers:every`,sonoagentgainsorlosesarepositoryatthatpublish.Forarolenamed`ghapp-devs`,thechecksays`Pinningreposonghappkeepstoday'sreach:ghapp-devsnowcoverseveryrepo.Narrowitinthisdraftorlater.`##StorethelistingcredentialStrazalistsghapp'srepositorieswithacredentialofitsown,thelistingcredential.Itsendsthatcredentialonitsowncallsto`repo_list`andonnoothercall,anditneverfallsbacktotheserver'sownsecretorarole's.Onitsside,ghappaccepts`GHAPP_MCP_LISTING_TOKEN`on`repo_list`aloneandanswersanyothertoolcalledwithit`Thelistingtokenopensrepo_listonly.CallthistoolthroughStraza.`Aleakedlistingtokenthereforeopensnowrite.CLIonly:Theconsolehasnoboxforthelistingcredential,sothissteprunsinaterminal.Yourterminal,administration:```shstrazactlappssecretsetghapp--listing``````textlistingcredentialsetfortheMCPserverghapp,fingerprint5606```Typethevalueof`GHAPP_MCP_LISTING_TOKEN`atthehiddenprompt.Theanswernamesthecredential'sfingerprint,neveritsvalue,andStrazalistsghapp'srepositorieswithinabout15seconds.Alistingcredentialcanbestoredonlyoncethetypeispinned,whichiswhythisstepfollowsthepublish.Untiloneisstored,`strazactlappsobjectsghapp`andtheRepostabsay`ghapphasnolistingcredential,soStrazacannotlistitsrepos.Addonewithstrazactlappssecretsetghapp--listing,thenrefreshwithstrazactlappsobjectsrefreshghapp.`##CheckthelistIntheconsole:Clickpathintheconsole:"MCPservers"›"ghapp"›"Repos"Thetabcarriesghapp'spluralandappearsoncethetypeispinned.Itsstripreads`ghapplistsitsreposwithrepo_list.`andthenthelastlistingwithitscount.EachrepositoryisonerowwiththecolumnsRepo,team,CoveredbyandInopendrafts,andthehelpiconbesideitsnamegivesitsid.Untilarolecoversit,arowreads`norole`and`Noagentreachesit.`underCoveredby.Inopendraftsnameseachopendraftyoumayreadthatwouldaddaroletotherepositoryordropone,suchas`Draft41addsghapp-write-payments`.WiththeCLI:Yourterminal,administration:```shstrazactlappsobjectsghapp``````tableghapplistsitsreposwithrepo_list.Lastlistinglessthanaminuteago:3repos.VALUEIDTITLETEAMCOVEREDBYghapp-straza-test/pay-api1425832684pay-apipaymentsnoroleghapp-straza-test/pay-web1425833185pay-webpaymentsnoroleghapp-straza-test/site1425833478sitewebnorole```Thefirstlinesayshowthelastlistingwent,withitscountofrepositories.ThetablehasthecolumnsVALUE,ID,TITLE,TEAMandCOVEREDBY,andarepositoryreads`norole`untilarolecoversit.Inthedemoorganization,`ghapp-straza-test/pay-api`and`ghapp-straza-test/pay-web`carrytheteam`payments`,and`ghapp-straza-test/site`carriestheteam`web`.Avalueinthelististheexactowner/nameacallmustcarry,byteforbyte,so`ghapp-straza-test/Pay-API`isanothernamethan`ghapp-straza-test/pay-api`.Strazalistsagainevery10minutes.Afteryoucreatearepository,`strazactlappsobjectsrefreshghapp`asksforalistingthatonereplicarunswithin30seconds,atmostonceaminuteperserver.##WritethetworolesEachrolenamesitstoolsandtherepositoriesitcovers.`ghapp-read`holdsthereadtoolsoneveryrepository,theonestheAppreacheslaterincluded.`ghapp-write-payments`holdsthewritetoolsonthetworepositoriesoftheteam`payments`,byname,andarepositoryaddedlaterstaysoutuntilapersonaddsit.Anagentthatholdsbothreadseverywhereandwritesinthosetworepositoriesonly.Intheconsole:Clickpathintheconsole:"Roles"›"Newrole"›"Applicationrole"On`Kind,serverandname`,pickghapp,type`write-payments`afteritsnameandpress**Next**.OnAccess,pick`Onlythetoolsyoutick`under**Whichtools**andtick`issue_comment`,`issue_create`,`pr_comment`,`pr_create`and`pr_merge`.Underthetools,**Onwhichrepos**says`ghappsays14ofitstoolsworkononerepo.Pickwhichreposthisrolereacheswiththem.`Itofferstwocards:|Card|Itsline||---|---||Everyrepo|`All`,today'scount,then`today,andeveryrepoghapplistslater.`||Theserepos|`Pickfromghapp'slist.Arepoaddedlaterstaysoutuntilyouaddit.`|Pick**Theserepos**andtick`ghapp-straza-test/pay-api`and`ghapp-straza-test/pay-web`inthelist,which`Filterrepos`narrows.With**Theserepos**picked,`repo_list`cannotbetickedandreads`repo_listcannotbeinaroleonsomerepos:nothingkeepsittothem.Makearoleoneveryrepoforit.`Thefold`Howitisstored`showstheaccessrowasarolefileholdsit.Press**Next**,readReview,andpress**Saveandpublish**.Make`ghapp-read`thesameway,with`branch_list`,`file_get`,`issue_get`,`issue_list`,`pr_get`,`pr_list`,`repo_get`,`search_code`and`search_issues`,and**Everyrepo**.Clickpathintheconsole:"Roles"›"ghapp-write-payments"›"Access"Thecolumn**Onwhichrepos**reads`2repos:ghapp-straza-test/pay-apiandghapp-straza-test/pay-web`.On`ghapp-read`itreads`everyrepo`.WiththeCLI:Aroleonapinnedserveriswrittenasarolefile,because`strazactlrolescreate`takesnocoversline.Bothfilesgointoonedraft:Yourterminal,administration:```shmkdir-pghapp-rolescat>ghapp-roles/ghapp-read.yaml<<'EOF'apiVersion:straza.dev/v1beta1kind:Rolemetadata:name:ghapp-readspec:kind:applicationdescription:Readtoolsofghapponeveryreposerver:ghappbindings:-app:ghapptools:-branch_list-file_get-issue_get-issue_list-pr_get-pr_list-repo_get-search_code-search_issuescovers:everyEOFcat>ghapp-roles/ghapp-write-payments.yaml<<'EOF'apiVersion:straza.dev/v1beta1kind:Rolemetadata:name:ghapp-write-paymentsspec:kind:applicationdescription:Writetoolsofghapponthepaymentsreposserver:ghappbindings:-app:ghapptools:-issue_comment-issue_create-pr_comment-pr_create-pr_mergecovers:objects:-ghapp-straza-test/pay-api-ghapp-straza-test/pay-webEOFstrazactldraftscheck-fghapp-roles/strazactldraftscreate-fghapp-roles/--note"Readoneveryrepo,writeonthepaymentsrepos"``````text+Role/ghapp-read+Role/ghapp-write-paymentsCheckedagainstlivestateat2026-10-1917:37UTC.infoRole/ghapp-readNobodyholdsghapp-readyet,soitreachesnothinguntilsomeoneisassignedit.infoRole/ghapp-write-paymentsNobodyholdsghapp-write-paymentsyet,soitreachesnothinguntilsomeoneisassignedit.Nothingwasstored.Thedocumentscanbepublished:strazactldraftscreate-fghapp-roles/Createddraft40with2documents.Nothingchangesuntilapersonpublishesit.+Role/ghapp-read+Role/ghapp-write-paymentsinfoRole/ghapp-readNobodyholdsghapp-readyet,soitreachesnothinguntilsomeoneisassignedit.infoRole/ghapp-write-paymentsNobodyholdsghapp-write-paymentsyet,soitreachesnothinguntilsomeoneisassignedit.Publishdraft40intheconsoleunderDrafts:http://localhost:8420/console/drafts/40````draftscheck`storesnothing,and`draftscreate`storesthedraft.Apersonpublishesitintheconsoleunder**Drafts**,becauseatokenneverpublishes.Thecheckrefusesarolethatnothingkeepstoitsrepositories,andeachrefusalsayswhattochange:|Therolefile|Thechecksays||---|---||hasno`covers`lineonitsbindingtoghapp|`ghapp-readbindstoolsthatworkonrepos.Saywhichrepositcovers:everyrepo,orthesereposbyname.Intherolefile,writecovers:every,orcovers:{objects:[<repo>,...]}.`||names`ghapp-straza-test/Pay-API`,whichdiffersincase|`ghapp-straza-test/Pay-APIisnotinghapp'srepolist.Comparethenamewithghapp'sRepostaborstrazactlappsobjectsghapp.Iftherepoisnew,refreshthelistwithstrazactlappsobjectsrefreshghapp,thentryagain.`||puts`repo_list`inaroleonsomerepos|`repo_listcannotbeinaroleonsomerepos:nothingkeepsittothem.Makearoleoneveryrepoforit.`||writes`covers:{every:true}`|`Roleghapp-readdoesnotreadasaRoledocument:line21:acoversmappingtakesonlythekeyobjects,andthisonecarriesevery.Writecovers:everyforeveryobject,orcovers:{objects:[<name>,...]}forsome.Compareitwithstrazactlrolesexportghapp-readandsendthedraftagain.`|Strazastoreseachnamedrepositorywithitsid.ArolethereforekeepscoveringarepositorythatGitHubrenames,andarepositorydeletedandmadeagainundertheoldnameisnotcoveredbytheoldline.ghapp'sownadminmaywrite`covers:every`onaroleghappownswhilethesetting`admin.serverAdminsCoverEvery`istrue,itsdefault.Setittofalsetoleave`every`toaglobaladmin,andthe**Everyrepo**cardthenreads`Onthisdeploymentonlyaglobaladminmaymakeghapp-readcovereveryrepo,includingthereposghapplistslater.Nametherepositcovers,oraskaglobaladmin.`[Configuration](https://docs.straza.ai/reference/configuration/index.md)liststhesetting.##GivetherolestoanagentinmidPointYourIGAtoolseeswhateachrolecovers.Strazarenderseachrole'sSCIMgroupwith`covers`,onevalueperaccessrowandnamedrepository.midPointmapsittotherole'sextensionitem`strazaCovers`andshowsitinthecolumnCoversofitsStrazaapplicationroleslist.Readthevaluesacertifiersees:Terminal,SCIM,asmidPointreadsit:```shforroleinghapp-readghapp-write-payments;docurl-s-H"Authorization:Bearer$ADMIN_API_TOKEN"-Ghttp://localhost:8420/scim/v2/Groups\--data-urlencode"filter=displayNameeq\"$role\""\|jq-c'.Resources[0]|{displayName,covers:.["urn:straza:params:scim:schemas:extension:2.0:Group"].covers}'done``````text{"displayName":"ghapp-read","covers":["ghapp:repoevery"]}{"displayName":"ghapp-write-payments","covers":["ghapp:repo\"ghapp-straza-test/pay-api\"","ghapp:repo\"ghapp-straza-test/pay-web\""]}````ADMIN_API_TOKEN`holdsatokenwiththescope`scim:read`,suchastheone[midPoint](https://docs.straza.ai/guides/connect-identity/midpoint/index.md#mint-the-token)mints.`ghapp-read`gives`ghapp:repoevery`.`ghapp-write-payments`givesonevalueperrepository,`ghapp:repo"ghapp-straza-test/pay-api"`and`ghapp:repo"ghapp-straza-test/pay-web"`,andthequoteskeeparepositorynamedeveryfrompassingforeveryrepository.Liketherestofthegroup'sextension,`covers`isread-only,andaPATCHonitanswers`400`with`scimType``mutability`.Assigntheimportedroles`AR:ghapp-read`and`AR:ghapp-write-payments`totheagentinmidPoint,directlyorthroughanaccesspackage,as[Assignanaccesspackage](https://docs.straza.ai/guides/connect-identity/midpoint/index.md#assign-an-access-package)shows.midPointwritesonemembershipperroleoverSCIM.Onthedemostack,thisassignsbothrolesto`joe-java-developer-agent`overmidPoint'sRESTinterface:Terminal,midPointREST,asthemidPointadmin:```shAGENT_OID=b6f9f2c8-0d5a-4e77-9a1e-9d3c0e5a1c4eforroleinAR:ghapp-readAR:ghapp-write-payments;doROLE_OID=fortryin$(seq190);doROLE_OID=$(curl-s-u'administrator:StrazaEval5ecr3t!'-H'Content-Type:application/xml'\-XPOST--data-binary"<q:queryxmlns:q=\"http://prism.evolveum.com/xml/ns/public/query-3\"><q:filter><q:equal><q:path>name</q:path><q:value>$role</q:value></q:equal></q:filter></q:query>"\http://localhost:8087/midpoint/ws/rest/roles/search|grep-o'oid="[0-9a-f-]\{36\}"'|head-1|cut-d'"'-f2)[-n"$ROLE_OID"]&&breaksleep2doneif[-z"$ROLE_OID"];thenecho"$roleisnotinmidPointafter180seconds,sonothingwasassigned.CheckthatmidPoint'sroleLiveSynctaskruns,thenrunthisagain."breakficat>assign.xml<<EOF<objectModificationxmlns="http://midpoint.evolveum.com/xml/ns/public/common/api-types-3"xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3"><itemDelta><t:modificationType>add</t:modificationType><t:path>c:assignment</t:path><t:value><c:targetRefoid="$ROLE_OID"type="c:RoleType"/></t:value></itemDelta></objectModification>EOFcurl-s-o/dev/null-w"$roleHTTP%{http_code}\n"-u'administrator:StrazaEval5ecr3t!'-H'Content-Type:application/xml'\-XPATCH--data-binary@assign.xmlhttp://localhost:8087/midpoint/ws/rest/users/$AGENT_OIDdone``````textAR:ghapp-readHTTP204AR:ghapp-write-paymentsHTTP204````AGENT_OID`istheagentinyourmidPoint.midPointimportsanewStrazaroleatitsnextroleLiveSync,whichrunsevery10secondsonthedemostack,sotheloopwaitsupto180secondsforeachimportedroletoappearbeforeitassignsit.Theagentholdsbothrolesfromitsnextcheck-in.ThecolumnCoversshowseachrole's`strazaCovers`,thesamevaluesSCIMgaveabove.ReadthemfrommidPoint'sRESTinterface:Terminal,midPointREST,asthemidPointadmin:```shforroleinAR:ghapp-readAR:ghapp-write-payments;docurl-s-u'administrator:StrazaEval5ecr3t!'-H'Content-Type:application/xml'-H'Accept:application/json'\-XPOST--data-binary"<q:queryxmlns:q=\"http://prism.evolveum.com/xml/ns/public/query-3\"><q:filter><q:equal><q:path>name</q:path><q:value>$role</q:value></q:equal></q:filter></q:query>"\http://localhost:8087/midpoint/ws/rest/roles/search\|jq-c'.object.object[0]|{name,strazaCovers:[.extension.strazaCovers]|flatten|map(.["@value"])}'done``````text{"name":"AR:ghapp-read","strazaCovers":["ghapp:repoevery"]}{"name":"AR:ghapp-write-payments","strazaCovers":["ghapp:repo\"ghapp-straza-test/pay-api\"","ghapp:repo\"ghapp-straza-test/pay-web\""]}```##ReadtherefusalCallawritetoolonarepositorythewriteroledoesnotcover.Fromtheagent'smachine,throughghapp'sownendpoint,thisreads`ghapp-straza-test/pay-api`andthentriestoopenapullrequestin`ghapp-straza-test/site`:Agent'smachine,client:```sh{printf'%s\n'\'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"docs-example","version":"1"}}}'\'{"jsonrpc":"2.0","method":"notifications/initialized"}'\'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"repo_get","arguments":{"repo":"ghapp-straza-test/pay-api"}}}'\'{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"pr_create","arguments":{"repo":"ghapp-straza-test/site","title":"BumptheGotoolchain","head":"bump-go","base":"main"}}}';sleep5;}\|strazamcpghapp``````text{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"extensions":{"io.modelcontextprotocol/ui":{}},"logging":{},"resources":{"listChanged":true},"tools":{"listChanged":true}},"protocolVersion":"2025-06-18","serverInfo":{"name":"straza","version":"v1.1.0-138-g8cd72d69"}}}{"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"Straza:pr_createonghapp-straza-test/sitewasrefused:noneofyourrolesonghappcoversthatrepo.Yourrolescoverpr_createonghapp-straza-test/pay-apiandghapp-straza-test/pay-web.Checktherepo'sname,oraskanadminforarolethatcoversit."}],"isError":true}}{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"ghapp-straza-test/pay-api(id1425832684):private,defaultbranch\"main\"."}],"structuredContent":{"archived":false,"defaultBranch":"main","fullName":"ghapp-straza-test/pay-api","htmlUrl":"https://github.com/ghapp-straza-test/pay-api","id":"1425832684","name":"pay-api","owner":"ghapp-straza-test","ownerType":"Organization","private":true}}}```Thefirstcallgoesthrough,because`ghapp-read`coverseveryrepository.Thesecondneverreachesghapp.Itsrefusalcomesbackbeforetheanswerto`repo_get`,becauseitneverleavesStraza.Strazaanswersitwithatoolresultmarkedasanerror,andtheagentreads`Straza:pr_createonghapp-straza-test/sitewasrefused:noneofyourrolesonghappcoversthatrepo.Yourrolescoverpr_createonghapp-straza-test/pay-apiandghapp-straza-test/pay-web.Checktherepo'sname,oraskanadminforarolethatcoversit.`Thatsentencenamesonlyrepositoriestheagent'sownrolescover,atmostten.Arepositorythatnoheldrolecoversandonethelistdoesnotholdgetthesameanswer,soanagentlearnsnothingaboutrepositoriesitcannotuse.Rolesoneveryrepositorycoveronethelistdoesnotholdyet,soarepositorycreatedaminuteagoworksatoncethroughsucharole.Whenarefusedcallnamesarepositorythelistdoesnothold,Strazaasksforalisting,atmostonceaminute,andthenewrepositorythenshowsinthelist,whereapersoncanaddittoarolethatnamesrepositories.Thecheckrunsbeforeanypolicy,sonopolicyrulecanallowacallitrefuses,andtherefusalspendsnoapproval.Intheconsole:Clickpathintheconsole:"Audit"›"deny"Thenewestrowreads`ghapp.pr_createonghapp-straza-test/site`under**What**,followedbythecall'sarguments,withtheagent'ssentenceunder**Reason**.Selectit.ThesheethasarownamedRepowiththevalueanditsidinghapp'slist.WiththeCLI:Yourterminal,administration:```shstrazactlaudittail--appghapp--typestraza.audit.mcp--limit3``````text#382[joe-java-developer-agent]{"data":{"app":"ghapp","count":14,"default":false,"effect":"allow","event":"tools.list","granted":false,"harness":"claude-code/mcp","reason":"catalogserved","ruleId":"","session":"01a156a5-c19a-7b1f-af1e-a4df00283dde","setName":"","snapshot":"de711b9f89fd7ef39105fa6f30103ce06449f1969206f97ffa8b71564c5eace1","tool":"","toolName":"","user":"01a1547c-366e-7767-97eb-caa598255d94"},"id":"ea5ff55c-a242-4fbc-be4f-e7c477802997","source":"strazad","specversion":"1.0","time":"2026-10-20T00:11:06.076296916Z","type":"straza.audit.mcp"}#383[joe-java-developer-agent]{"data":{"app":"ghapp","arguments":"{\"repo\":\"ghapp-straza-test/pay-api\"}","bindingId":"01a1553d-bc97-7c68-a755-4d26e5d08b2f","credentialId":"01a1553c-ae88-7e94-9afc-dc6cfc5d14be","credentialSource":"shared","default":true,"effect":"allow","event":"tool.pre","granted":true,"harness":"claude-code/mcp","object":{"id":"1425832684","reach":"argument","type":"repo","value":"ghapp-straza-test/pay-api"},"reason":"allowedbyroleaccess.Nopolicyrulegatesthistool.","role":"ghapp-read","ruleId":"","session":"01a156a5-c19a-7b1f-af1e-a4df00283dde","setName":"","snapshot":"de711b9f89fd7ef39105fa6f30103ce06449f1969206f97ffa8b71564c5eace1","tool":"mcp.call","toolName":"repo_get","user":"01a1547c-366e-7767-97eb-caa598255d94"},"id":"7443f5cc-7af8-493b-b12b-8a7a50c302a9","source":"strazad","specversion":"1.0","time":"2026-10-20T00:11:06.086979293Z","type":"straza.audit.mcp"}#384[joe-java-developer-agent]{"data":{"app":"ghapp","arguments":"{\"repo\":\"ghapp-straza-test/site\",\"title\":\"BumptheGotoolchain\",\"head\":\"bump-go\",\"base\":\"main\"}","default":false,"effect":"deny","event":"tool.pre","granted":false,"harness":"claude-code/mcp","object":{"id":"1425833478","reach":"argument","type":"repo","value":"ghapp-straza-test/site"},"reason":"Straza:pr_createonghapp-straza-test/sitewasrefused:noneofyourrolesonghappcoversthatrepo.Yourrolescoverpr_createonghapp-straza-test/pay-apiandghapp-straza-test/pay-web.Checktherepo'sname,oraskanadminforarolethatcoversit.","rolesChecked":["ghapp-write-payments"],"ruleId":"","session":"01a156a5-c19a-7b1f-af1e-a4df00283dde","setName":"","snapshot":"de711b9f89fd7ef39105fa6f30103ce06449f1969206f97ffa8b71564c5eace1","tool":"mcp.call","toolName":"pr_create","user":"01a1547c-366e-7767-97eb-caa598255d94"},"id":"fdadeb1e-3f0b-42f6-8159-a1392a6bf3c3","source":"strazad","specversion":"1.0","time":"2026-10-20T00:11:06.093335815Z","type":"straza.audit.mcp"}```Thedenyrecordcarries`object`,withthetype`repo`,thereach`argument`,thevalue`ghapp-straza-test/site`anditsidinghapp'slist,`1425833478`.Italsocarries`rolesChecked`with`ghapp-write-payments`,theheldrolethatbinds`pr_create`.Ithasno`role`andno`bindingId`,becausenoroleadmittedthecall.Acoveredcallthenmeetsyourpolicieslikeanyothercall.Anapprovalthatapolicyasksforonsuchacallholdsforthatrepositoryalone,becauseStrazatiestheapprovaltotherepository'stypeandvalue.Theapprovalcardnamestherepositoryonlyinsidethecall'sarguments,sowith`approval.preview.enabled`settofalsetheapproverdoesnotseeit.[Events](https://docs.straza.ai/reference/events/index.md)listseveryfieldoftherecord.##Whatghapppromises{.nostep}Strazacheckstherepositoryacallnames,andghappkeepsthecalltothatrepository.EverymarkedservermakesthreepromisesthatStrazacannotcheck:acalltouchesonlytheobjectitsargumentnames,thevaluenamesexactlyoneobjectatthetimeofthecall,andtheserveracceptscallsonlyfromthegateway.[Objectmarksforserverauthors](https://docs.straza.ai/reference/object-marks/index.md#promises)statesthemforaserver'sauthor.ghappkeepstheminfourways.ItcomparesthenamewithGitHub'sownspellingbyteforbyteandneverfollowsarenameredirect.Whenaroleonsomerepositoriesadmittedthecall,Strazasendstherepository'sidfromitslistas`params._meta["ai.straza/object-id"]`,andghapprefusesthecallwhenthenamenowleadstoanotherid,suchasarepositorydeletedandmadeagain.ItthenmakesaGitHubtokenforthatonerepositorywithonlythetool'spermissions.Itslastguardisthecaller:ghappacceptsthegatewaysecret,atokenfromalistedidentityproviderclientwhoseonlycredentialisStraza'sclientassertionkey,andthelistingtokenon`repo_list`alone.Strazachecksrepositoriesinitsgatewayonly,anditsharnesshooksneverrunthischeck.Anagentthatreachedghappsomeotherwaywouldmeetnorepositorycheck,whichiswhythethirdpromisematters.[Thesecuritymodel](https://docs.straza.ai/security/security-model/index.md#the-object-boundary)drawsthatboundary.##Next{.nostep}-[Objects](https://docs.straza.ai/concepts/objects/index.md)explainsthemodelbehindthesesteps.-[Objectmarksforserverauthors](https://docs.straza.ai/reference/object-marks/index.md)isthecontractforaserverthatwantsthesamelimits.-[Accessperrole](https://docs.straza.ai/guides/serve-mcp-apps/catalogs-per-role/index.md)previewswhichtoolsarole'ssessionsseeandputsatoolbehindapproval.-[midPoint](https://docs.straza.ai/guides/connect-identity/midpoint/index.md)handstherolesoutthroughaccesspackagesandtakesthemawayagain.


Walked on v1.0.0-1561-g98d49634 on 2026-10-20, except the console steps, read from source, and the last line of each draft's output, written from the code. The enterprise demo stack on Rocky Linux 10.1, with ghapp started beside it from its own deploy kit on a private GitHub App installed on the organization ghapp-straza-test. Every strazactl step but the two publishes ran on an admin API token, and the note strazactl then printed on stderr on a token is left out of the output. Both drafts were then published on the admin's own strazactl login, with y answered at the prompt. strazactl no longer signs in or publishes, so a person publishes each draft in the console, and the last line of each draft's output, which names that way, was written from the code on 2026-11-01 and not run. Both secrets were set from STRAZA_SECRET_VALUE, so no hidden prompt shows. The SCIM read ran on a token with scim:read alone. midPoint had imported both roles before the assignment loop ran, so it did not wait. The four refusals in the table were checked with strazactl drafts check on edited copies of the two role files. The agent's calls ran as joe-java-developer-agent through straza mcp ghapp in the demo stack's harness container, a Debian 12 machine with straza v1.1.0-138-g8cd72d69. The console steps were read from the console's source and not clicked. A fix then changed three sentences, so the second and fourth rows of the refusal table, the agent's calls and the audit records were walked again on 2026-10-20 at v1.0.0-1561-g98d49634. The other steps ran on 2026-10-19 at v1.0.0-1556-ge9afd09a.

