detached abe47d3

feat(api): add branch prefixes to tokens

commit
abe47d3bceb4c048138640a0c3b98d2597e7642b
author
brice · · 2026-10-12 14:47 UTC
parent
97a9365
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +52 -10
M gen/ermite/token.pb.go +38 -9 generated file: collapsed
M proto/ermite/token.proto +14 -1
@@ -58,13 +58,19 @@ message Token {
5858 // LastUsedAt is when the token last authenticated a request. Unset if never
5959 // used. It may lag behind the real time by a short delay.
6060 google.protobuf.Timestamp last_used_at = 7;
61+ // BranchPrefixes limits the branches the token may write. Each prefix ends
62+ // with "/" (for example "agent/claude/"). Empty means every branch. A limited
63+ // token can push, force-push and delete only branches under its prefixes; it
64+ // cannot push tags or other refs, create repositories, or merge into or
65+ // delete a branch outside its prefixes.
66+ repeated string branch_prefixes = 8;
6167 }
6268
6369 // TokenService manages stored tokens. Every RPC needs the token:admin scope.
6470 //
6571 // A token can only create tokens that are within its own limits: scopes that
6672 // it holds, repositories that it may access and an expiry no later than its
67-// own. The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never
73+// own (including its branch prefixes). The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never
6874 // listed.
6975 service TokenService {
7076 // CreateToken creates a token and returns its secret. The secret is shown
@@ -92,6 +98,13 @@ message CreateTokenRequest {
9298 // ExpiresAt is when the token stops working. Unset means no expiry. It must
9399 // be in the future.
94100 google.protobuf.Timestamp expires_at = 4;
101+ // BranchPrefixes limits the branches the token may write, for example
102+ // "agent/claude/". Each prefix must end with "/" and be a valid branch name
103+ // once the "/" is removed; at most 8 are allowed. Empty means every branch;
104+ // a limited token cannot push tags or create repositories. A token that has
105+ // prefixes can only create tokens whose prefixes each lie within its own.
106+ // Fails with invalid_argument when a prefix is malformed.
107+ repeated string branch_prefixes = 5;
95108 }
96109
97110 // CreateTokenResponse holds the new token and its secret.
@@ -58,13 +58,19 @@ message Token {
58 // LastUsedAt is when the token last authenticated a request. Unset if never58 // LastUsedAt is when the token last authenticated a request. Unset if never
59 // used. It may lag behind the real time by a short delay.59 // used. It may lag behind the real time by a short delay.
60 google.protobuf.Timestamp last_used_at = 7;60 google.protobuf.Timestamp last_used_at = 7;
61+ // BranchPrefixes limits the branches the token may write. Each prefix ends
62+ // with "/" (for example "agent/claude/"). Empty means every branch. A limited
63+ // token can push, force-push and delete only branches under its prefixes; it
64+ // cannot push tags or other refs, create repositories, or merge into or
65+ // delete a branch outside its prefixes.
66+ repeated string branch_prefixes = 8;
61 }67 }
62 68
63 // TokenService manages stored tokens. Every RPC needs the token:admin scope.69 // TokenService manages stored tokens. Every RPC needs the token:admin scope.
64 //70 //
65 // A token can only create tokens that are within its own limits: scopes that71 // A token can only create tokens that are within its own limits: scopes that
66 // it holds, repositories that it may access and an expiry no later than its72 // it holds, repositories that it may access and an expiry no later than its
67-// own. The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never73+// own (including its branch prefixes). The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never
68 // listed.74 // listed.
69 service TokenService {75 service TokenService {
70 // CreateToken creates a token and returns its secret. The secret is shown76 // CreateToken creates a token and returns its secret. The secret is shown
@@ -92,6 +98,13 @@ message CreateTokenRequest {
92 // ExpiresAt is when the token stops working. Unset means no expiry. It must98 // ExpiresAt is when the token stops working. Unset means no expiry. It must
93 // be in the future.99 // be in the future.
94 google.protobuf.Timestamp expires_at = 4;100 google.protobuf.Timestamp expires_at = 4;
101+ // BranchPrefixes limits the branches the token may write, for example
102+ // "agent/claude/". Each prefix must end with "/" and be a valid branch name
103+ // once the "/" is removed; at most 8 are allowed. Empty means every branch;
104+ // a limited token cannot push tags or create repositories. A token that has
105+ // prefixes can only create tokens whose prefixes each lie within its own.
106+ // Fails with invalid_argument when a prefix is malformed.
107+ repeated string branch_prefixes = 5;
95 }108 }
96 109
97 // CreateTokenResponse holds the new token and its secret.110 // CreateTokenResponse holds the new token and its secret.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 18

↑↓ moveenter openesc close