docs(guides): slice 1 identities round 2; lead decision 58
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -32,47 +32,69 @@ GITEA=https://git.example # your Gitea base URL
|
||||
EXP=$(date -u -d '+90 days' +%Y-%m-%dT00:00:00Z) # Vikunja token expiry
|
||||
```
|
||||
|
||||
Write `EXP` down. It goes in the business file as each Vikunja token's
|
||||
`expires`.
|
||||
Write down the date part of `EXP`. It goes in the business file as
|
||||
each Vikunja token's `expires`, in `YYYY-MM-DD` form. The broker treats
|
||||
00:00Z on that date as the expiry, the same instant `EXP` names.
|
||||
|
||||
The commands need curl 7.76 or later, for `--fail-with-body`.
|
||||
|
||||
## 1. Gitea: four bot users and their tokens
|
||||
|
||||
Gitea tokens don't expire, and the HTTP route for creating one needs a
|
||||
password, so this part uses the web UI.
|
||||
|
||||
1. As a site admin, create the users `pm-bot`, `cto-bot`, `coder-bot`
|
||||
and `reviewer-bot` (Site Administration, User Accounts, Create). Give
|
||||
1. As a site admin, create the users `mosaic-stack-pm-bot`,
|
||||
`mosaic-stack-cto-bot`, `mosaic-stack-coder-bot` and
|
||||
`mosaic-stack-reviewer-bot` (Site Administration, User Accounts,
|
||||
Create). Gitea usernames are global to the instance, so the business
|
||||
id in the name keeps one business's bots, and revoking them, apart
|
||||
from another's. Give
|
||||
each a long random password, which you won't need again after step 3.
|
||||
None of them is an admin.
|
||||
2. In `mosaicstack/stack`, add each bot as a collaborator:
|
||||
|
||||
| Bot | Repository access |
|
||||
|---|---|
|
||||
| pm-bot | Write (labels, assignees and closing need it) |
|
||||
| cto-bot | Write |
|
||||
| coder-bot | Write |
|
||||
| reviewer-bot | Read |
|
||||
| mosaic-stack-pm-bot | Write (labels, assignees and closing need it) |
|
||||
| mosaic-stack-cto-bot | Write |
|
||||
| mosaic-stack-coder-bot | Write |
|
||||
| mosaic-stack-reviewer-bot | Read |
|
||||
|
||||
No bot is on a protected branch's push or merge allowlist. Pushing to
|
||||
a protected branch is a gated action, and it stays Jason's.
|
||||
No bot is on a protected branch's push, merge or approvals allowlist.
|
||||
Pushing to a protected branch is a gated action, and merges stay
|
||||
Jason's. Reviewer-bot's approvals in Gitea are advisory and don't
|
||||
count toward required approvals. The review verdict that gates a row
|
||||
lives in the queue and the bus.
|
||||
3. Log in as each bot, go to Settings, Applications, and generate one
|
||||
token named `mosaic-stack-<role>-<date>` with these scopes:
|
||||
|
||||
| Bot | Scopes |
|
||||
|---|---|
|
||||
| pm-bot | `write:issue`, `read:repository`, `read:user` |
|
||||
| cto-bot | `write:issue`, `write:repository`, `read:user` |
|
||||
| coder-bot | `write:issue`, `write:repository`, `read:user` |
|
||||
| reviewer-bot | `write:issue`, `write:repository`, `read:user` |
|
||||
| mosaic-stack-pm-bot | `write:issue`, `read:repository`, `read:user` |
|
||||
| mosaic-stack-cto-bot | `write:issue`, `write:repository`, `read:user` |
|
||||
| mosaic-stack-coder-bot | `write:issue`, `write:repository`, `read:user` |
|
||||
| mosaic-stack-reviewer-bot | `write:issue`, `write:repository`, `read:user` |
|
||||
|
||||
Leave every other scope unset, and set none to "public only".
|
||||
Reviewer-bot's `write:repository` is there because Gitea files pull
|
||||
request reviews under the repository scope. Its Read collaborator
|
||||
access is what stops it pushing. Row SR's review checks that before
|
||||
anyone relies on it.
|
||||
4. Copy each token into its file with an editor, never `echo`:
|
||||
`$S/pm-gitea.token`, `$S/cto-gitea.token`, `$S/coder-gitea.token`,
|
||||
`$S/reviewer-gitea.token`. One line, the token only.
|
||||
Reviewer-bot's `write:repository` is there because Gitea checks pull
|
||||
request reviews against the repository scope. Its Read collaborator
|
||||
access stops pushes to branches and tags. It does not stop an AGit
|
||||
push to `refs/for/<branch>`, which opens a pull request. That's
|
||||
acceptable only because the broker holds the token and offers the
|
||||
reviewer no push action.
|
||||
4. Write each token to its file from a silent prompt, in the same
|
||||
`umask 077` shell. Paste the token at the prompt and press Enter:
|
||||
|
||||
```sh
|
||||
for r in pm cto coder reviewer; do
|
||||
printf '%s token: ' "$r"; read -rs t; echo
|
||||
printf %s "$t" > "$S/$r-gitea.token"; unset t
|
||||
done
|
||||
```
|
||||
|
||||
`read` and `printf` are shell builtins, so the value never reaches
|
||||
`ps` or the history. Don't use an editor, which can leave a swap or
|
||||
backup copy behind.
|
||||
5. Log out of each bot account. Record today's date as each Gitea
|
||||
token's `rotateBy` base. Section 5 has the rotation schedule.
|
||||
|
||||
@@ -110,9 +132,13 @@ docker exec -it mosaic-vikunja /app/vikunja/vikunja user create -u jason -e you@
|
||||
VK=http://127.0.0.1:3456
|
||||
```
|
||||
|
||||
`user create` asks for the password when you leave out `-p`. If your
|
||||
build doesn't prompt, stop and set it some other way. Don't put the
|
||||
password on the command line.
|
||||
`user create` asks for the password when you leave out `-p`. Keep `-it`,
|
||||
because without a terminal the command exits instead of prompting. Never
|
||||
put the password on the command line.
|
||||
|
||||
Both mounts are required. The container runs as your uid, and
|
||||
`/app/vikunja` isn't writable to it, so Vikunja needs the files mount
|
||||
for its startup write check and the db mount for its database.
|
||||
|
||||
### Owner login for this guide
|
||||
|
||||
@@ -135,8 +161,8 @@ print("owner header written")
|
||||
PY
|
||||
```
|
||||
|
||||
The probes logged in at `/api/v2/login`. The `token` field name is
|
||||
assumed from v1, so the script stops if it's missing. Section 4 deletes
|
||||
The `token` field is confirmed in the v2.7.0 source (`auth_login.go`).
|
||||
The script still stops if it's missing. Section 4 deletes
|
||||
the header file.
|
||||
|
||||
## 3. Vikunja: the project, the board and the bots
|
||||
@@ -150,9 +176,9 @@ In the web UI, as the owner:
|
||||
`blocked`. Each title appears once.
|
||||
3. In the view's settings, check that the done bucket is `done` and the
|
||||
default bucket is `todo`. Leave bucket configuration on manual.
|
||||
4. Create the labels the business file template lists, and note each id.
|
||||
The business file names labels by id until row S3 settles which
|
||||
labels a bot can see.
|
||||
4. Slice 1 needs no labels, since the requirement id lives in the task,
|
||||
not in a label. If you create any, note each id. The business file
|
||||
names labels by id until row S3 settles which labels a bot can see.
|
||||
|
||||
```sh
|
||||
P=<project id>
|
||||
@@ -213,8 +239,10 @@ mint() { # mint ROLE BOT_ID SCOPES_FILE
|
||||
local r=$1 id=$2 sc=$3 resp="$S/.mint-$1.json"
|
||||
jq -n --arg t "$BIZ-$r-$(date +%F)" --argjson o "$id" --arg e "$EXP" --slurpfile p "$sc" \
|
||||
'{title:$t, owner_id:$o, expires_at:$e, permissions:$p[0]}' |
|
||||
api -X POST "$VK/api/v2/tokens" --data-binary @- -o "$resp" &&
|
||||
jq -j .token "$resp" > "$S/$r-vikunja.token" &&
|
||||
curl -s --fail-with-body -H @"$S/vikunja-owner.hdr" -H 'Content-Type: application/json' \
|
||||
-X POST "$VK/api/v2/tokens" --data-binary @- -o "$resp" ||
|
||||
{ jq -c '{code, message}' "$resp"; rm -f "$resp"; return 1; }
|
||||
jq -je '.token | strings' "$resp" > "$S/$r-vikunja.token" &&
|
||||
jq -c '{id, owner_id, expires_at, starts_tk: (.token | startswith("tk_"))}' "$resp"
|
||||
rm -f "$resp"
|
||||
}
|
||||
@@ -226,9 +254,11 @@ mint sync <sync id> "$S/scopes-sync.json"
|
||||
```
|
||||
|
||||
Check that each line shows `starts_tk: true` and the `expires_at` you
|
||||
set. Vikunja accepts a past expiry without complaint, so read it. A 400
|
||||
with code 14002 means a scope name is wrong. Fix the scope file and mint
|
||||
again.
|
||||
set. Vikunja accepts a past expiry without complaint, so read it. On an
|
||||
error, `mint` prints the response's `code` and `message`, which carry no
|
||||
token. Code 14002 means a scope name is wrong. Fix the scope file and
|
||||
mint again. If the response has no `token` field, `mint` writes nothing
|
||||
usable and stops; delete the empty file before you retry.
|
||||
|
||||
## 4. Check and clean up
|
||||
|
||||
@@ -261,14 +291,17 @@ role. The stack never writes this file.
|
||||
|
||||
## 5. Rotation
|
||||
|
||||
- **Vikunja**, before `expires`: log in as the owner (section 2), mint a
|
||||
new token for the same bot with `mint`, update `expires` in the
|
||||
business file, and restart the broker. Then revoke the old token:
|
||||
- **Vikunja**, before `expires`: in a new shell, rerun section 0, the
|
||||
owner login in section 2, and the `api` and `mint` definitions in
|
||||
section 3. Mint a new token for the same bot, update `expires` in the
|
||||
business file, restart the broker, and finish with section 4's
|
||||
`rm -f`. Then revoke the old token:
|
||||
`api -X DELETE "$VK/api/v2/tokens/<old id>"`. The broker refuses to
|
||||
start with a token past its `expires`, and it treats any 401 as a
|
||||
refusal, never a retry.
|
||||
- **Gitea**, every 90 days or at once if a token may have leaked: log in
|
||||
as the bot, generate the replacement, write it to the file, restart
|
||||
as the bot, generate the replacement, write it to the file with the
|
||||
`read -rs` loop from section 1, restart
|
||||
the broker, then delete the old token in the same settings page.
|
||||
Update `rotateBy`. Each rotation gets one line in
|
||||
`docs/SESSIONS.md`: date, who, which identities, and no values.
|
||||
|
||||
Reference in New Issue
Block a user