88 lines
2.8 KiB
Markdown
88 lines
2.8 KiB
Markdown
|
|
# Operator Ingress Gate
|
||
|
|
|
||
|
|
Transport-agnostic, idempotent write gate for all operator-originated Gitea mutations.
|
||
|
|
|
||
|
|
## Purpose
|
||
|
|
|
||
|
|
This is the **single canonical write path** for any external command that creates, updates, or merges Gitea issues/PRs. Whether the command arrives via Nostr DM, Matrix, Telegram, or a local script, it must pass through this gate.
|
||
|
|
|
||
|
|
## Design Invariants
|
||
|
|
|
||
|
|
1. **Deterministic idempotency key** — SHA-256 of `(source, action, repo, sorted_payload)`.
|
||
|
|
2. **Local ledger deduplication** — First line of defense; replayed commands return the original ACK instantly.
|
||
|
|
3. **Gitea-side probing** — Second line of defense; scans existing issues/comments for duplicates before mutating.
|
||
|
|
4. **Proof-of-execution** — Every mutation embeds the gate-key in the Gitea comment/issue body, creating an audit trail.
|
||
|
|
5. **Replay safety** — Running the same command twice never double-writes.
|
||
|
|
|
||
|
|
## Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
operator-gate/
|
||
|
|
├── gitea_gate.py # Core gate logic (Command, Ack, GiteaGate)
|
||
|
|
├── adapters/
|
||
|
|
│ └── nostur_adapter.py # Nostr DM adapter
|
||
|
|
├── tests/
|
||
|
|
│ └── test_gate_idempotency.py
|
||
|
|
└── README.md # This file
|
||
|
|
```
|
||
|
|
|
||
|
|
## Supported Actions
|
||
|
|
|
||
|
|
| Action | Payload keys |
|
||
|
|
|---------------|-------------------------------------------|
|
||
|
|
| `create_issue`| `title`, `body`, `assignees[]`, `labels[]`|
|
||
|
|
| `add_comment` | `issue_num`, `body` |
|
||
|
|
| `close_issue` | `issue_num` |
|
||
|
|
| `assign_issue`| `issue_num`, `assignees[]` |
|
||
|
|
| `merge_pr` | `pr_num` |
|
||
|
|
|
||
|
|
## Usage
|
||
|
|
|
||
|
|
```python
|
||
|
|
from gitea_gate import create_issue, add_comment
|
||
|
|
|
||
|
|
ack = create_issue(
|
||
|
|
source="nostr:npub10trqk...",
|
||
|
|
repo="Timmy_Foundation/timmy-home",
|
||
|
|
title="Fix relay timeout",
|
||
|
|
body="The nostr relay drops connections after 30s.",
|
||
|
|
assignees=["allegro"],
|
||
|
|
)
|
||
|
|
|
||
|
|
print(ack.gitea_url) # Canonical URL of the created issue
|
||
|
|
print(ack.idempotency_key) # 32-char fingerprint
|
||
|
|
```
|
||
|
|
|
||
|
|
## Nostur Adapter
|
||
|
|
|
||
|
|
The Nostur adapter polls the local Nostr relay (`ws://localhost:2929`) for Kind-4 DMs from authorized operator pubkeys. It normalizes DM text into gate commands:
|
||
|
|
|
||
|
|
- `status` → queue summary for `the-nexus`
|
||
|
|
- `create <repo> <title>` → create issue
|
||
|
|
- `comment <repo> #<num> <text>` → add comment
|
||
|
|
- `close <repo> #<num>` → close issue
|
||
|
|
- `assign <repo> #<num> <user1,user2>` → assign issue
|
||
|
|
- `merge <repo> #<num>` → merge PR
|
||
|
|
|
||
|
|
Run it:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python3 operator-gate/adapters/nostur_adapter.py
|
||
|
|
```
|
||
|
|
|
||
|
|
## Running Tests
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd operator-gate
|
||
|
|
python3 -m unittest tests.test_gate_idempotency -v
|
||
|
|
```
|
||
|
|
|
||
|
|
## Future Adapters
|
||
|
|
|
||
|
|
- Matrix bot adapter
|
||
|
|
- Telegram bot adapter
|
||
|
|
- Local CLI adapter (`hermes operator-do ...`)
|
||
|
|
|
||
|
|
---
|
||
|
|
*Built for the Timmy Foundation fleet. Sovereignty and service always.*
|