developerz.ai is designed around the principle that a maintainer agent must be auditable, least-privilege, and honest, or it cannot be trusted in an OSS repo.
GitHub App permissions
This is the complete set the App requests. You can check it against the source at
any time: GitHub renders the live list on the App's own page,
github.com/apps/developerz-ai, and serves
it as JSON from https://api.github.com/apps/developerz-ai. Neither needs an account.
| Scope | Level | Used for |
|---|---|---|
metadata: read |
Repository | Required by GitHub for every App. |
issues: write |
Repository | Post triage comments, apply and remove labels, close duplicates. |
pull_requests: write |
Repository | Post reviews and review comments, and merge a PR once the machine gates pass. |
contents: write |
Repository | Read .maintainer.yml, manifests and diffs; push the branch a dispatched agent works on; create tags and GitHub Releases. |
workflows: write |
Repository | Included in that push: git refuses a push that touches .github/workflows/** without it. |
statuses: read |
Repository | Read the combined commit status, so a repo whose CI reports via the Status API is graded rather than read as "no CI". |
actions: read |
Repository | Receive workflow_run / workflow_job events, which is how CI jobs reach the fleet. |
deployments: read |
Repository | Receive deployment / deployment_status events, so a merged PR can be joined to a deploy verdict. |
checks: write |
Repository | List a commit's check runs to grade CI, and publish our own check run named developerz.ai review on the pull request's head sha so a review in flight is visible. It concludes success or neutral, never failure. |
members: read |
Organisation | Read your role in an org at sign-in, to decide whether you get owner or member on that tenant. See teams & roles. |
discussions: write |
Repository | Requested, not currently used. |
emails: read |
Account | Requested, not currently used. |
The rows marked not currently used are exactly that: requested at install time, reached by no code path today. We would rather name them here than describe a narrower App than the one you are actually installing; dropping them from the App is tracked as its own change.
The members: read read is deliberately least-privilege in its failure mode: if your
org has not approved the permission, GitHub declines to tell us your role and we read
that as no signal, which lands you on member rather than owner. A refusal never
elevates.
Webhook events (issues, pull requests, pushes) are delivered through the App's event
subscriptions, gated by the permissions above. GitHub Apps have no separate webhooks
scope for receiving events.
What that means concretely. The App can push code, which is how a dispatched
coding agent opens a PR, and it can merge one, but only on the machine gates
(auto_merge: true, CI green, review verdict, branch protections satisfied), never
because a model decided to. It can create tags and GitHub Releases on a repo whose
.maintainer.yml asks for them; a repo that ships no policy file gets no releases at
all. It does not request administration or secrets, so it cannot change repo
settings, read your Actions secrets, or manage access. It reaches only the
repositories you explicitly grant at install time.
This is a decision, not a gap we haven't gotten to. Creating a brand-new
github.com/<org>/<new-repo> on your behalf would need administration: write, and we've
chosen not to request it: widening a scope re-prompts every existing installation for
consent, and "the App can create, and therefore delete, repositories" is a bigger reach
than the rest of this table. The path we ship instead: you create the empty repo yourself
(one click, or gh repo create), then ask us to scaffold it: no new permission required,
and it works on personal accounts too. If that ever changes, this table changes in the
same PR. See docs/idea/security.md
for the full reasoning.
Secrets and credentials
All credentials stored by developerz.ai are encrypted at rest:
- Model keys (BYOK): envelope encryption. A per-row data key encrypts the value, and a key-encryption key wraps that data key, both AES-256-GCM with their own nonce. Decrypted in memory only at the moment of an API call; never logged or included in audit events.
- Webhook tokens: same scheme. Transmitted only outbound to the registered webhook URL over HTTPS.
- GitHub installation tokens: short-lived (one hour), minted on demand from the GitHub API and held in memory only, refreshed before expiry, and never written to the database or to disk.
Secrets live in Postgres on our managed Kubernetes cluster. The database has no public listener and is reachable only from inside the cluster network.
The audit log
Every action taken by the agent is written to the append-only audit log before the action is executed. The log is the source of truth for what the agent did, when, and why.
Each entry records:
| Field | Description |
|---|---|
id |
Row id. |
session_id |
The agent session the event belongs to. |
repo_full_name |
owner/repo, or null for a repo-less session. |
kind |
Event type, dotted: session.boot, model.call, tool.call.ok, tool.call.err, policy.gate.refused, escalation.fired, handoff.fired, account.paused, and so on. |
tool |
The tool name on a tool-call row, null otherwise. |
input |
The tool call or decision input (sanitised; no secrets). |
output |
The result (GitHub API response code, comment ID, etc.). |
ts |
UTC timestamp. |
Rows also carry a prev_hash, the SHA-256 of the previous row for that session.
The log is a hash chain, not just an append-only table: a removed or edited
row breaks the chain and is detectable, and the database refuses two rows off the
same predecessor so a chain cannot silently fork.
Append-only is enforced, not merely intended. No UPDATE or DELETE is issued
against audit_events, the session foreign key is ON DELETE RESTRICT so the
rows outlive attempts to delete what they describe, and there is no retention
job: no cron prunes this table, deliberately, because the audit is the
attestation the other prune lanes are allowed to reference.
The dashboard exposes a read-only view. Raw rows are read through
GET /v1/audit/events, newest-first and keyset-paginated (follow next_cursor);
a token needs read:sessions to call it.
Bot disclosure
When the agent acts as itself it always discloses. Every comment it posts under the GitHub App carries this footer, appended by the comment tools rather than written by the model, so a model that forgets it cannot ship an undisclosed comment:
馃 Posted by developerz.ai, the maintainer agent, not a human.
GitHub also attributes those comments to developerz-ai[bot]; the footer is the second
layer, in the prose a reader actually reads.
There is one other identity lane, and it is not the bot. If you automate your own account with your own fine-grained personal access token, GitHub attributes the comment to you, and no bot footer is appended, because a person speaking as themselves is not the agent acting as itself. That lane is deliberately fenced: approving, reviewing and merging are refused on it, since a user's own token "approving" an agent's PR would fake a human review and satisfy branch protections nobody exercised. Those actions belong to the disclosed App identity alone.
What holds in both lanes: the agent never impersonates a human, and it never omits the footer on the App lane.
No bot-on-bot loops
The agent detects comments from other bots (CodeRabbit, Copilot, Dependabot, Renovate)
and defers to them rather than creating conflicting threads. A comment from developerz-ai[bot]
never triggers another loop iteration.
Responsible disclosure
Found a security issue in developerz.ai itself? Email security@developerz.ai. We aim to acknowledge within 24 hours and resolve critical issues within 72 hours. Please do not open a public GitHub issue for security reports.
Data handling
- Issue and PR text is sent to the inference provider you selected, see BYOK for the full list, under your own API account. Your provider's data-handling terms apply.
- Repository code is not stored by developerz.ai; only file diffs relevant to the current event are forwarded to the model.
- Audit logs are kept for the life of the account. Nothing prunes them, by design: see the audit log section above. There is no retention dial to turn down, so plan for the rows persisting rather than expiring.
- For data-residency requirements, contact hello@developerz.ai.