Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34c2f7d634 | ||
|
|
fc53251285 | ||
|
|
6d0cb3bba3 |
@@ -96,6 +96,32 @@ docker network create web
|
|||||||
13. **Firewall**: `sudo ufw allow 80/tcp 443/tcp`; once the HTTPS routes above are all
|
13. **Firewall**: `sudo ufw allow 80/tcp 443/tcp`; once the HTTPS routes above are all
|
||||||
confirmed working, `sudo ufw delete allow 3000/tcp` and `sudo ufw delete allow 9443/tcp`.
|
confirmed working, `sudo ufw delete allow 3000/tcp` and `sudo ufw delete allow 9443/tcp`.
|
||||||
|
|
||||||
|
14. **Branch protection on `main`**, on every repo you want the bot working on (so it's
|
||||||
|
structurally limited to opening PRs, never merging or pushing directly) — via the repo's
|
||||||
|
Settings → Branches → Add Rule, or the API:
|
||||||
|
```bash
|
||||||
|
curl -X POST -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" \
|
||||||
|
"$GITEA_URL/api/v1/repos/<owner>/<repo>/branch_protections" \
|
||||||
|
-d '{
|
||||||
|
"rule_name": "main",
|
||||||
|
"enable_push": false,
|
||||||
|
"enable_merge_whitelist": true,
|
||||||
|
"merge_whitelist_usernames": ["<your-human-username>"]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
This blocks *all* direct pushes to `main` (everyone goes through a PR) and restricts
|
||||||
|
merging to the human usernames listed — `claude-bot` (see below) is never in that list,
|
||||||
|
so it structurally cannot merge, only open PRs, regardless of what its token can do.
|
||||||
|
|
||||||
|
15. **Dedicated bot account** (recommended over using your own account/token for the
|
||||||
|
agent): create a separate Gitea user (e.g. `claude-bot`) via Site Administration → User
|
||||||
|
Accounts, generate its own token (scopes: repository, issue, package — no `admin`,
|
||||||
|
`organization`, or `user` needed), add it as a repo Collaborator with **Write**
|
||||||
|
permission on each automated repo, then use its token as `GITEA_TOKEN` (agent) and
|
||||||
|
`REGISTRY_TOKEN` (CI secret) instead of your own. This makes every PR comment, branch,
|
||||||
|
and PR clearly attributed to the bot instead of you, and its access is easy to revoke
|
||||||
|
independently.
|
||||||
|
|
||||||
## Smoke test
|
## Smoke test
|
||||||
|
|
||||||
- Open a throwaway PR on a repo with the PR webhook set → expect a Claude review comment.
|
- Open a throwaway PR on a repo with the PR webhook set → expect a Claude review comment.
|
||||||
|
|||||||
+14
-2
@@ -1,4 +1,4 @@
|
|||||||
import { MatrixClient, SimpleFsStorageProvider, AutojoinRoomsMixin } from "matrix-bot-sdk";
|
import { MatrixClient, SimpleFsStorageProvider } from "matrix-bot-sdk";
|
||||||
import { runChatTask } from "./runner.js";
|
import { runChatTask } from "./runner.js";
|
||||||
import { askOpenRouter, DEFAULT_MODEL } from "./openrouter.js";
|
import { askOpenRouter, DEFAULT_MODEL } from "./openrouter.js";
|
||||||
|
|
||||||
@@ -45,7 +45,19 @@ export async function startMatrixBot() {
|
|||||||
|
|
||||||
const storage = new SimpleFsStorageProvider("/workspace/matrix-bot-storage.json");
|
const storage = new SimpleFsStorageProvider("/workspace/matrix-bot-storage.json");
|
||||||
const client = new MatrixClient(HOMESERVER_URL, ACCESS_TOKEN, storage);
|
const client = new MatrixClient(HOMESERVER_URL, ACCESS_TOKEN, storage);
|
||||||
AutojoinRoomsMixin.setupOnClient(client);
|
|
||||||
|
// Not using AutojoinRoomsMixin: it calls /joined_rooms at startup to build its initial
|
||||||
|
// state, which — like the /whoami call removed earlier — 404s against Continuwuity for
|
||||||
|
// reasons unrelated to the endpoint itself (curling it directly works fine). This
|
||||||
|
// simpler handler does the one thing we actually need — auto-join on invite — without
|
||||||
|
// that startup scan.
|
||||||
|
client.on("room.invite", async (roomId) => {
|
||||||
|
try {
|
||||||
|
await client.joinRoom(roomId);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("failed to join invited room", roomId, err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
client.on("room.message", async (roomId, event) => {
|
client.on("room.message", async (roomId, event) => {
|
||||||
// Invite-only control room enforces who can reach the bot at all; this just scopes
|
// Invite-only control room enforces who can reach the bot at all; this just scopes
|
||||||
|
|||||||
Reference in New Issue
Block a user