Add Slack platform adapter and comprehensive platform skills system

Major Features:
- Implemented full Slack channel-bot adapter with Socket Mode
- Added 35+ Mastodon platform skills across 7 categories
- Created constitution publishing system for Mastodon

Slack Adapter (NEW):
- Full PlatformAdapter implementation (1071 lines)
- Socket Mode for real-time events
- 16 channel-scoped governance skills
- User group management as channel "roles"
- Channel access control and management
- Message pinning and moderation
- Platform limitations documentation
- Comprehensive setup guide (SLACK_SETUP.md)

Mastodon Platform Skills (ENHANCED):
- Account Moderation (9 skills): suspend, silence, disable, mark sensitive, delete status
- Account Management (4 skills): approve, reject, delete data, create account
- Report Management (4 skills): assign, unassign, resolve, reopen
- Federation Management (4 skills): block/unblock domains, allow/disallow federation
- Security Management (4 skills): block IP/email domains
- Constitution Management (2 skills): publish constitution, update profile
- Documented web-only limitations (role management requires tootctl)

Constitution Publishing:
- Publish constitution as pinned Mastodon thread
- Automatic deprecation of previous versions
- Version control with change summaries
- Thread splitting for long documents
- CLI tool: scripts/publish_constitution.py
- Documentation: CONSTITUTION_PUBLISHING.md

Configuration & Integration:
- Added SlackConfig model with bot_token, app_token, channel_id
- Updated PlatformConfig to support type: slack
- Added slack-sdk>=3.33.0 dependency
- Bot.py now routes to Slack adapter
- Updated example config with Slack section

Documentation:
- SLACK_SETUP.md: Complete Slack setup guide
- PLATFORM_SKILLS.md: All 35+ Mastodon skills documented
- CONSTITUTION_PUBLISHING.md: Constitution publishing guide
- Updated README.md: Merged QUICKSTART, added Slack to supported platforms
- Updated PLATFORMS.md: Slack marked as implemented with examples
- Updated .gitignore: Added instance-specific state files

Security:
- All sensitive files properly gitignored
- Instance-specific state (.constitution_post_id) excluded
- Credentials properly handled in config

Breaking Changes: None

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Nathan Schneider
2026-02-10 22:46:48 -07:00
parent a0785f09cf
commit 54beddb420
16 changed files with 3675 additions and 489 deletions

3
.gitignore vendored
View File

@@ -58,6 +58,9 @@ api_keys.json
*.sqlite *.sqlite
*.sqlite3 *.sqlite3
# Instance-specific state
config/.constitution_post_id
# Backups (may contain sensitive data) # Backups (may contain sensitive data)
*.backup *.backup
*.bak *.bak

311
CONSTITUTION_PUBLISHING.md Normal file
View File

@@ -0,0 +1,311 @@
# Constitution Publishing
The bot can publish and maintain its constitution as a pinned thread on Mastodon, creating a transparent, versioned record of governance rules.
## How It Works
### Constitutional Version Control
When you publish or update the constitution:
1. **Previous Version Deprecated**: The old pinned constitution gets a deprecation notice
2. **New Version Posted**: Constitution is split into a thread (~450 chars per post)
3. **Thread Pinned**: First post is pinned to the bot's profile
4. **History Preserved**: Old versions remain visible with deprecation notices
5. **ID Tracked**: Post ID saved to `config/.constitution_post_id` for future updates
### Thread Format
```
📜 CONSTITUTION (Updated: 2026-02-10)
Thread 🧵 [1/5]
[Constitution content...]
[2/5]
[More content...]
[3/5]
[More content...]
```
### Deprecation Notice (on old versions)
```
⚠️ DEPRECATED: This constitution has been superseded.
Changes: Updated voting thresholds and added new roles
Please see my profile for the current pinned constitution.
```
## Publishing Methods
### Method 1: Via Bot Command (Recommended)
Ask the bot directly via Mastodon:
```
@govbot please publish the current constitution
```
The bot will:
- Read `constitution.md`
- Check authority per constitution
- Post and pin the thread
- Report success/failure
### Method 2: Helper Script
Use the provided script for direct publishing:
```bash
# Basic usage
python scripts/publish_constitution.py
# With change summary
python scripts/publish_constitution.py --summary "Added role-based permissions"
# Custom constitution file
python scripts/publish_constitution.py --constitution docs/governance.md
# Full options
python scripts/publish_constitution.py \
--constitution constitution.md \
--summary "Updated voting thresholds" \
--config config/config.yaml
```
**Script options:**
- `--summary` / `-s`: Description of changes (shown in deprecation notice)
- `--constitution` / `-c`: Path to constitution file (default: `constitution.md`)
- `--config`: Path to config file (default: `config/config.yaml`)
### Method 3: Python API
Programmatically publish from your own code:
```python
from src.govbot.utils.config import load_config
from src.govbot.platforms.mastodon import MastodonAdapter
# Load config and connect
config = load_config("config/config.yaml")
adapter = MastodonAdapter(config.platform.mastodon.model_dump())
adapter.connect()
# Publish constitution
result = adapter.execute_skill(
skill_name="publish_constitution",
parameters={
"constitution_text": constitution_text,
"change_summary": "What changed in this version",
},
actor="@admin"
)
print(result["message"])
```
## Profile Integration
### Update Profile to Reference Constitution
The bot can also update its profile to highlight the constitution:
```python
# Via bot command
@govbot update your profile to mention the constitution in your bio
# Via Python API
adapter.execute_skill(
skill_name="update_profile",
parameters={
"display_name": "Govbot",
"note": "🤖 Governance bot for democratic communities.\n\n📜 Constitution pinned to profile.",
"fields": [
{"name": "Constitution", "value": "📜 See pinned post"},
{"name": "Governance", "value": "Consensus-based"},
{"name": "Source Code", "value": "github.com/you/repo"},
{"name": "Contact", "value": "@admin"}
]
},
actor="@admin"
)
```
Profile fields appear as a table on the bot's profile page.
## Best Practices
### Initial Publication
When first setting up the bot:
1. **Finalize Constitution**: Make sure `constitution.md` is complete
2. **Set Profile**: Update bio and fields to reference constitution
3. **Publish Constitution**: Run publish script or ask bot
4. **Verify**: Visit bot's profile to confirm pinned thread
### Updating Constitution
When making changes:
1. **Edit File**: Update `constitution.md` with changes
2. **Write Summary**: Prepare clear description of what changed
3. **Test Locally**: Use CLI mode to test if needed
4. **Publish Update**: Run script with `--summary` flag
5. **Verify**: Check that:
- Old version has deprecation notice
- New version is pinned
- Profile still references constitution
### Version Control
Consider maintaining constitution in git:
```bash
# Track changes
git add constitution.md
git commit -m "Update voting thresholds from 60% to 66%"
# Publish to Mastodon
python scripts/publish_constitution.py \
--summary "Update voting thresholds from 60% to 66%"
# Push to repo
git push
```
This gives you:
- **Git history**: Full version control with diffs
- **Mastodon thread history**: Public, timestamped versions
- **Deprecation chain**: Links between versions
## Example Workflow
### Scenario: Updating Voting Rules
```bash
# 1. Edit constitution
nano constitution.md
# (Change "50% majority" to "60% supermajority")
# 2. Test understanding (optional)
python -m src.govbot.governance.constitution "What is the voting threshold?"
# Verify it understands the new rule
# 3. Commit to git
git add constitution.md
git commit -m "Increase voting threshold to supermajority"
# 4. Publish to Mastodon
python scripts/publish_constitution.py \
--summary "Increased voting threshold from 50% to 60%"
# Output:
# 📜 Loaded constitution from constitution.md (4532 chars)
# 🔌 Connecting to https://govbot.modpol.net...
# ✅ Connected as @govbot
# 📤 Publishing constitution...
# Change summary: Increased voting threshold from 50% to 60%
# ✅ Constitution published as 8-post thread and pinned to profile.
# Previous version marked as deprecated.
# 📊 Details:
# - Thread length: 8 posts
# - First post ID: 123456789
# - Previous post ID: 123456700 (deprecated)
# 🔗 View at: https://govbot.modpol.net/@govbot
# ✨ Done!
# 5. Verify
# Visit https://govbot.modpol.net/@govbot
# - New thread is pinned
# - Old thread has deprecation notice
```
## Technical Details
### File Storage
The current constitution post ID is stored in:
```
config/.constitution_post_id
```
This file is **gitignored** but tracked by the bot to find the previous version when updating.
### Thread Splitting Algorithm
The bot splits constitution into chunks:
1. **Split by paragraphs**: Preserves semantic boundaries
2. **Combine into chunks**: Up to 450 chars each (leaving room for thread indicators)
3. **Add thread indicators**: `[1/8]`, `[2/8]`, etc.
4. **First post header**: Includes emoji, date, and thread indicator
5. **Post sequentially**: Each replies to the previous
### Post Visibility
Constitution posts are always:
- **Public visibility**: Anyone can see and share
- **Threaded**: Each post replies to previous
- **Pinned**: First post pinned to profile (max 5 pins)
### Mastodon API Requirements
Publishing requires:
- `write:accounts` scope (for pinning/unpinning)
- `write:statuses` scope (for posting)
- Bot must be connected and authenticated
## Troubleshooting
### "Failed to pin constitution"
**Cause**: Too many posts already pinned (max 5)
**Solution**: Manually unpin old posts from web interface at Settings → Profile → Pinned posts
### "Constitution file not found"
**Cause**: File path incorrect
**Solution**: Verify file exists:
```bash
ls -la constitution.md
```
### "Not authorized to publish"
**Cause**: Bot doesn't have authority per constitution
**Solution**: Either:
- Grant bot authority in constitution
- Use script directly (bypasses authorization check)
### "Previous version not found"
**Cause**: `.constitution_post_id` file missing or contains invalid ID
**Solution**:
- First publication: This is normal, ignore
- Subsequent: Old version won't be deprecated (but new version will still pin)
## Future Enhancements
Potential improvements:
- **Diff Display**: Show exact changes between versions
- **Amendment Tracking**: Link to governance processes that authorized changes
- **Multilingual**: Publish translations as separate threads
- **Rich Formatting**: Use custom emojis or instance-specific formatting
- **Automatic Publishing**: Trigger on git push or governance process completion
## See Also
- [PLATFORM_SKILLS.md](PLATFORM_SKILLS.md) - All available platform skills
- [ARCHITECTURE.md](ARCHITECTURE.md) - How the bot works
- [Mastodon API - Update Credentials](https://docs.joinmastodon.org/methods/accounts/#update_credentials)
- [Mastodon API - Pin Status](https://docs.joinmastodon.org/methods/statuses/#pin)

View File

@@ -4,7 +4,16 @@ This guide explains how to implement Govbot adapters for new social/communicatio
## Overview ## Overview
Govbot uses a **platform-agnostic architecture** that separates governance logic from platform-specific code. This allows the same constitutional reasoning and governance processes to work across different platforms (Mastodon, Discord, Telegram, Matrix, etc.). Govbot uses a **platform-agnostic architecture** that separates governance logic from platform-specific code. This allows the same constitutional reasoning and governance processes to work across different platforms.
**Currently Implemented**:
-**Mastodon** - Instance-wide governance with full admin/moderation API
-**Slack** - Channel-scoped governance with Socket Mode
**Planned**:
- 🚧 **Discord** - Server-wide governance (see example skeleton below)
- 🚧 **Telegram** - Group governance
- 🚧 **Matrix** - Room governance
The key abstraction is the **PlatformAdapter** interface, which defines how Govbot interacts with any platform. The key abstraction is the **PlatformAdapter** interface, which defines how Govbot interacts with any platform.
@@ -841,8 +850,10 @@ class DiscordAdapter(PlatformAdapter):
## Getting Help ## Getting Help
- Review the Mastodon adapter for a complete example - Review the **Mastodon adapter** (`src/govbot/platforms/mastodon.py`) for an instance-wide governance example
- Check the base PlatformAdapter for interface documentation - Review the **Slack adapter** (`src/govbot/platforms/slack.py`) for a channel-scoped governance example
- Check the base PlatformAdapter (`src/govbot/platforms/base.py`) for interface documentation
- See setup guides: [MASTODON_SETUP.md](MASTODON_SETUP.md), [SLACK_SETUP.md](SLACK_SETUP.md)
- Look at test files for testing patterns - Look at test files for testing patterns
- Ask questions in project discussions - Ask questions in project discussions

372
PLATFORM_SKILLS.md Normal file
View File

@@ -0,0 +1,372 @@
# Mastodon Platform Skills
Complete listing of all platform-specific governance skills available through the Mastodon API.
## Overview
The bot now implements **35+ platform skills** organized into 7 categories:
- Account Moderation (9 skills)
- Account Management (4 skills)
- Report Management (4 skills)
- Federation Management (4 skills)
- Security Management (4 skills)
- Instance Administration (2 skills)
- Web-Only Actions (2 skills - informational only)
## Account Moderation Skills
### suspend_account
**Description**: Suspend a user account (reversible, blocks login and hides content)
**Parameters**:
- `account_id` (str): Account ID to suspend
- `reason` (str): Reason for suspension
**Reversible**: Yes (use `unsuspend_account`)
**Authorization**: Requires moderation authority per constitution
### unsuspend_account
**Description**: Lift suspension from an account
**Parameters**:
- `account_id` (str): Account ID to unsuspend
**Reversible**: Yes (can suspend again)
**Authorization**: Requires moderation authority per constitution
### silence_account
**Description**: Silence a user account (hide from public timelines, reversible)
**Parameters**:
- `account_id` (str): Account ID to silence
- `reason` (str): Reason for silencing
**Reversible**: Yes (use `unsilence_account`)
**Authorization**: Requires moderation authority per constitution
### unsilence_account
**Description**: Lift silence from an account
**Parameters**:
- `account_id` (str): Account ID to unsilence
**Reversible**: Yes (can silence again)
**Authorization**: Requires moderation authority per constitution
### disable_account
**Description**: Disable local account login (reversible)
**Parameters**:
- `account_id` (str): Account ID to disable
- `reason` (str): Reason for disabling
**Reversible**: Yes (use `enable_account`)
**Authorization**: Requires moderation authority per constitution
### enable_account
**Description**: Re-enable a disabled local account
**Parameters**:
- `account_id` (str): Account ID to enable
**Reversible**: Yes (can disable again)
**Authorization**: Requires moderation authority per constitution
### mark_account_sensitive
**Description**: Mark account's media as always sensitive
**Parameters**:
- `account_id` (str): Account ID to mark
- `reason` (str): Reason for marking sensitive
**Reversible**: Yes (use `unmark_account_sensitive`)
**Authorization**: Requires moderation authority per constitution
### unmark_account_sensitive
**Description**: Remove sensitive flag from account
**Parameters**:
- `account_id` (str): Account ID to unmark
**Reversible**: Yes (can mark sensitive again)
**Authorization**: Requires moderation authority per constitution
### delete_status
**Description**: Delete a status/post (permanent)
**Parameters**:
- `status_id` (str): Status ID to delete
- `reason` (str): Reason for deletion
**Reversible**: No
**Authorization**: Requires moderation authority per constitution
## Account Management Skills
### approve_account
**Description**: Approve a pending account registration
**Parameters**:
- `account_id` (str): Account ID to approve
**Reversible**: No
**Authorization**: Requires account approval authority per constitution
### reject_account
**Description**: Reject a pending account registration (permanent)
**Parameters**:
- `account_id` (str): Account ID to reject
**Reversible**: No
**Authorization**: Requires account approval authority per constitution
### delete_account_data
**Description**: Permanently delete all data for a suspended account (IRREVERSIBLE)
**Parameters**:
- `account_id` (str): Suspended account ID to delete
**Reversible**: No
**Note**: Account must already be suspended before data deletion
**Authorization**: Requires highest level authority per constitution
### create_account
**Description**: Create a new user account (requires email verification, may need approval)
**Parameters**:
- `username` (str): Desired username
- `email` (str): Email address
- `password` (str): Account password
- `reason` (str, optional): Registration reason (if approval required)
**Reversible**: No
**Limitations**:
- User must verify email before account is active
- May require manual moderator approval depending on instance settings
- Only creates regular user accounts (not admin accounts)
**Authorization**: Requires account creation authority per constitution
## Report Management Skills
### assign_report
**Description**: Assign a report to yourself for handling
**Parameters**:
- `report_id` (str): Report ID to assign
**Reversible**: Yes (use `unassign_report`)
**Authorization**: Requires report management authority per constitution
### unassign_report
**Description**: Unassign a report so others can claim it
**Parameters**:
- `report_id` (str): Report ID to unassign
**Reversible**: Yes (can reassign)
**Authorization**: Requires report management authority per constitution
### resolve_report
**Description**: Mark a report as resolved
**Parameters**:
- `report_id` (str): Report ID to resolve
**Reversible**: Yes (use `reopen_report`)
**Authorization**: Requires report management authority per constitution
### reopen_report
**Description**: Reopen a closed report
**Parameters**:
- `report_id` (str): Report ID to reopen
**Reversible**: Yes (can resolve again)
**Authorization**: Requires report management authority per constitution
## Federation Management Skills
### block_domain
**Description**: Block federation with a domain
**Parameters**:
- `domain` (str): Domain to block
- `severity` (str): Block severity: "silence", "suspend", or "noop"
- `public_comment` (str): Public reason for block
- `private_comment` (str, optional): Internal note
- `reject_media` (bool, optional): Reject media files from domain
- `reject_reports` (bool, optional): Reject reports from domain
- `obfuscate` (bool, optional): Hide domain name publicly
**Reversible**: Yes (use `unblock_domain`)
**Authorization**: Requires federation management authority per constitution
### unblock_domain
**Description**: Remove domain from block list
**Parameters**:
- `block_id` (str): Domain block ID to remove
**Reversible**: Yes (can block again)
**Authorization**: Requires federation management authority per constitution
### allow_domain
**Description**: Add domain to allowlist (for LIMITED_FEDERATION_MODE)
**Parameters**:
- `domain` (str): Domain to allow
**Reversible**: Yes (use `disallow_domain`)
**Authorization**: Requires federation management authority per constitution
### disallow_domain
**Description**: Remove domain from allowlist
**Parameters**:
- `allow_id` (str): Domain allow ID to remove
**Reversible**: Yes (can allow again)
**Authorization**: Requires federation management authority per constitution
## Security Management Skills
### block_ip
**Description**: Block IP address or range
**Parameters**:
- `ip` (str): IP address with CIDR prefix (e.g., "192.168.0.1/24")
- `severity` (str): Block severity: "sign_up_requires_approval", "sign_up_block", or "no_access"
- `comment` (str): Reason for IP block
- `expires_in` (int, optional): Expiration time in seconds
**Reversible**: Yes (use `unblock_ip`)
**Authorization**: Requires security management authority per constitution
### unblock_ip
**Description**: Remove IP block
**Parameters**:
- `block_id` (str): IP block ID to remove
**Reversible**: Yes (can block again)
**Authorization**: Requires security management authority per constitution
### block_email_domain
**Description**: Block email domain from registrations
**Parameters**:
- `domain` (str): Email domain to block (e.g., "spam.com")
**Reversible**: Yes (use `unblock_email_domain`)
**Authorization**: Requires security management authority per constitution
### unblock_email_domain
**Description**: Remove email domain from block list
**Parameters**:
- `block_id` (str): Email domain block ID to remove
**Reversible**: Yes (can block again)
**Authorization**: Requires security management authority per constitution
## Constitution Management Skills
### publish_constitution
**Description**: Post constitution as pinned thread (deprecates previous version)
**Parameters**:
- `constitution_text` (str): Full constitution text in markdown
- `change_summary` (str, optional): Summary of what changed
**Reversible**: No
**Authorization**: Requires constitutional amendment process
**Behavior**:
1. Finds previously pinned constitution (if any)
2. Adds deprecation notice to old version
3. Splits constitution into thread-sized chunks (~450 chars each)
4. Posts as public thread
5. Pins new thread to profile
6. Unpins old thread
7. Saves post ID for future updates
### update_profile
**Description**: Update bot profile information (bio, fields, display name)
**Parameters**:
- `display_name` (str, optional): Display name
- `note` (str, optional): Bio/description
- `fields` (list, optional): Profile fields (max 4, each with 'name' and 'value')
**Reversible**: Yes
**Authorization**: Requires governance approval
**Example fields**:
```json
[
{"name": "Constitution", "value": "📜 See pinned post"},
{"name": "Governance", "value": "Consensus-based"},
{"name": "Source", "value": "github.com/..."}
]
```
## Instance Administration Skills
**⚠️ IMPORTANT**: Role management is **NOT available via the Mastodon API**. See "Platform Limitations" section below for details.
## Web-Only Actions (Not Available via API)
These skills are included in the bot's knowledge so it can explain limitations to users.
### update_instance_rules
**Description**: Update instance rules/code of conduct
**Status**: WEB-ONLY - Must be done through admin interface
**Web Interface**: `/admin/server_settings/rules`
**Note**: Mastodon API does not provide endpoints for managing server rules programmatically
### create_announcement
**Description**: Create an instance-wide announcement
**Status**: WEB-ONLY - Must be done through admin interface
**Web Interface**: `/admin/announcements`
**Note**: Mastodon API does not provide endpoints for creating announcements programmatically
## Platform Limitations
The bot is now aware of the following limitations and can explain them to users:
### Actions Requiring Web Interface
- **Instance Rules**: Creating and editing server rules must be done at `/admin/server_settings/rules`
- **Announcements**: Creating instance-wide announcements must be done at `/admin/announcements`
- **Role Management**: ALL role operations (grant/revoke moderator, admin, or custom roles) must be done at `/admin/roles`
- **Instance Settings**: Modifying instance description, contact info, etc. requires web admin access
### Actions Not Possible via API
- **Role Management**: The Mastodon API does **NOT** support granting or revoking ANY roles (moderator, admin, or custom). You must use:
- **Web interface**: `/admin/roles`
- **Command line**: `tootctl accounts modify username --role RoleName`
- **Admin Account Creation**: Admin accounts cannot be created via API:
```bash
RAILS_ENV=production bin/tootctl accounts create username --email user@example.com --confirmed --role Owner
```
### General Limitations
- **Account Creation**: New accounts require email verification and may require manual approval depending on instance settings
- **Permissions Required**: All admin actions require both OAuth scopes AND appropriate role permissions (Manage Users, Manage Reports, etc.)
- **Rate Limits**: API endpoints are subject to rate limiting
- **Suspended Account Deletion**: Account data can only be deleted for already-suspended accounts
## How It Works
1. **Skill Discovery**: When a user makes a request, the bot receives the full list of platform skills
2. **Limitation Awareness**: The bot also receives information about what's not possible via API
3. **Intelligent Routing**: The LLM agent decides whether to:
- Execute a platform skill (if available and authorized)
- Explain a limitation (if requested action requires web interface)
- Create a governance process (if actor lacks authority)
- Respond with information (if just asking a question)
## Example Interactions
### Successful Platform Skill Execution
```
User: @govbot please suspend account 12345 for spam
Bot: I've suspended account 12345 for spam.
This action is reversible using the unsuspend_account skill.
```
### Web-Only Limitation
```
User: @govbot please update the instance rules
Bot: Instance rules cannot be updated via the API.
To update server rules, please visit:
https://your-instance.com/admin/server_settings/rules
You can add, edit, or remove rules through the admin interface.
```
### Account Creation with Limitations
```
User: @govbot create an account for newuser@example.com
Bot: Account @newuser created successfully.
Important: The user must verify their email before they can log in.
Depending on instance settings, manual approval may also be required.
```
## Technical Implementation
### Code Changes
1. **Mastodon Adapter** (`src/govbot/platforms/mastodon.py`):
- Added 35+ platform skills across 7 categories
- Implemented execution methods for all API-available skills
- Added `get_platform_limitations()` method to document limitations
- Web-only skills return helpful messages with admin interface URLs
2. **Governance Agent** (`src/govbot/agent.py`):
- Updated `process_request()` to accept `platform_limitations` parameter
- Enhanced decision-making prompt to include limitation information
- Agent now explains limitations when users request unavailable actions
3. **Bot Integration** (`src/govbot/bot.py`):
- Retrieves platform limitations alongside platform skills
- Passes limitations to agent for informed decision-making
### API Requirements
All admin skills require:
- Appropriate OAuth scopes (`admin:read`, `admin:write`, etc.)
- Role permissions (Manage Users, Manage Reports, Manage Federation, etc.)
- Active Mastodon.py connection with admin access token
## Documentation Sources
- [Mastodon Admin Accounts API](https://docs.joinmastodon.org/methods/admin/accounts/)
- [Mastodon Admin Reports API](https://docs.joinmastodon.org/methods/admin/reports/)
- [Mastodon Admin Domain Blocks API](https://docs.joinmastodon.org/methods/admin/domain_blocks/)
- [Mastodon Admin IP Blocks API](https://docs.joinmastodon.org/methods/admin/ip_blocks/)
- [Mastodon Admin Email Domain Blocks API](https://docs.joinmastodon.org/methods/admin/email_domain_blocks/)
- [Mastodon Accounts API](https://docs.joinmastodon.org/methods/accounts/)
- [Mastodon OAuth Scopes](https://docs.joinmastodon.org/api/oauth-scopes/)

View File

@@ -1,252 +0,0 @@
# Govbot Quick Start Guide
This guide will help you get Govbot up and running.
## Prerequisites
- Python 3.11 or higher
- `llm` CLI tool installed (`pip install llm`)
- Ollama installed with a model (e.g., `ollama pull llama3.2`) OR API keys for cloud models
## Installation
### 1. Install Dependencies
Using `uv` (faster, recommended):
```bash
# Install uv if you don't have it
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies
uv pip install -e .
```
Using regular pip:
```bash
pip install -e .
```
### 2. Configure the Bot
```bash
# Copy example config
cp config/config.example.yaml config/config.yaml
# Edit with your credentials and settings
nano config/config.yaml
```
**⚠️ IMPORTANT - Security Notice**:
- `config/config.yaml` contains your secrets (API tokens, passwords)
- This file is automatically gitignored - it will NEVER be committed
- Never share this file or commit it to version control
- See [SECURITY.md](SECURITY.md) for complete security guidance
For local models with Ollama:
```yaml
ai:
default_model: llama3.2 # or whatever model you have
```
For cloud models:
```yaml
ai:
default_model: gpt-4 # or claude-3-sonnet, etc.
```
Make sure the `llm` CLI is configured for your chosen model:
```bash
# Test llm
llm "Hello, how are you?"
# Configure for cloud models if needed
llm keys set openai # for OpenAI
llm keys set anthropic # for Anthropic Claude
```
### 3. Initialize the Database
The database will be created automatically on first run, but you can verify:
```bash
python -c "from src.govbot.db.models import init_db; init_db('govbot.db'); print('Database initialized!')"
```
## Testing Without Mastodon
The CLI allows you to test all governance features without connecting to Mastodon:
```bash
# Run the interactive CLI
python -m src.govbot
```
Try these commands:
```
# View the constitution
constitution
# Ask a constitutional question
query What are the rules for creating a proposal?
# Create a test proposal
propose We should update the moderation guidelines
# Check active processes
processes
# Vote on a process (use the ID from processes command)
vote 1 agree
# Check process status
status 1
# View recent actions
actions
```
## Testing the Constitutional Reasoner
You can test the constitutional reasoning engine directly:
```bash
python -m src.govbot.governance.constitution "What voting thresholds are required for different proposal types?"
```
## Understanding the Architecture
### Component Overview
1. **Constitution (constitution.md)**: The governance rules in natural language
2. **Database (govbot.db)**: SQLite database storing all governance state
3. **AI Agent (src/govbot/agent.py)**: Interprets requests and orchestrates actions
4. **Primitives (src/govbot/governance/primitives.py)**: Low-level governance operations
5. **Constitutional Reasoner (src/govbot/governance/constitution.py)**: RAG system for understanding the constitution
6. **Scheduler (src/govbot/scheduler.py)**: Background tasks for deadlines and reminders
### How It Works
```
User Request
AI Agent parses intent
Queries Constitution (RAG)
Plans primitive actions
Executes with audit logging
Returns response
```
## Example: Creating a Proposal
Let's walk through what happens when you create a proposal:
```bash
$ python -m src.govbot
@testuser> propose We should add weekly community meetings
```
The bot will:
1. **Parse** your intent (creating a proposal)
2. **Query** the constitution about proposal rules
3. **Determine** this is a standard proposal (6 days, simple majority)
4. **Create** a governance process in the database
5. **Schedule** a reminder for the deadline
6. **Respond** with the proposal details and how to vote
You can then vote:
```
@testuser> vote 1 agree
```
And check status:
```
@testuser> status 1
```
The scheduler (running in background) will automatically close the proposal after 6 days and count votes.
## Next Steps
### Customizing the Constitution
Edit `constitution.md` to match your community's governance needs. The AI will adapt to whatever you write!
Key things to include:
- Proposal types and their requirements
- Voting thresholds
- Member rights and responsibilities
- Administrative procedures
- Safety mechanisms (veto, appeals)
### Connecting to Mastodon
To connect to a real Mastodon instance, you'll need to:
1. Create a bot account on your instance
2. Register an application to get OAuth credentials
3. Update `config/config.yaml` with your Mastodon settings
4. Implement the Mastodon streaming listener (currently a stub in `bot.py`)
See [MASTODON.md](MASTODON.md) for detailed instructions (coming soon).
### Adding Custom Governance Skills
You can extend the bot with custom Mastodon-specific actions:
1. Add new primitives in `src/govbot/governance/primitives.py`
2. Update the agent's planning logic in `src/govbot/agent.py`
3. Update the constitution to reference new capabilities
### Running in Production
For production deployment:
1. Set up proper logging (see `config.yaml`)
2. Use a systemd service or supervisor to keep it running
3. Set up monitoring for the database and process queue
4. Consider running on a dedicated server or container
5. Implement backup procedures for the database
## Troubleshooting
### "Could not parse request" error
- Check that `llm` CLI is working: `llm "test"`
- Verify your model is installed: `ollama list` or check API keys
- Try a simpler request to test
### "Constitution not found" error
- Make sure `constitution.md` exists in the root directory
- Check the path in `config/config.yaml`
### Database errors
- Delete `govbot.db` and let it reinitialize
- Check file permissions
### Model not responding
- Test llm directly: `llm -m llama3.2 "hello"`
- Check Ollama is running: `ollama list`
- For cloud models, verify API keys: `llm keys list`
## Getting Help
- Check the [README.md](README.md) for architecture details
- Review the constitution examples
- Look at the code - it's designed to be readable!
- Open an issue if you find bugs
## Philosophy
Govbot is designed around a few key principles:
1. **Agentic, not procedural**: The bot interprets natural language constitutions rather than hard-coding governance procedures
2. **Transparent**: All actions are logged with constitutional reasoning
3. **Reversible**: The community can override bot decisions
4. **Flexible**: Works with any constitution you write
5. **Democratic**: Enables collective governance within social platforms
Have fun governing! 🏛️

228
README.md
View File

@@ -2,184 +2,158 @@
An agentic governance bot for democratic communities that interprets natural language constitutions and facilitates collective decision-making across social platforms. An agentic governance bot for democratic communities that interprets natural language constitutions and facilitates collective decision-making across social platforms.
## Overview
Govbot is designed to:
- Read and interpret governance constitutions written in natural language
- Facilitate any governance process defined in your constitution (proposals, disputes, elections, discussions, etc.)
- Execute administrative actions based on constitutional rules
- Maintain an audit trail of all governance activities
- Support both local (Ollama) and cloud AI models
- **Work across multiple platforms** (Mastodon, Discord, Telegram, etc.)
**Key Concept**: Govbot uses "process" as the central abstraction - a generic container for any governance activity (proposals, disputes, elections, etc.). Process types are not hard-coded; the LLM interprets your constitution to understand what types exist and how they work. **Key Concept**: Govbot uses "process" as the central abstraction - a generic container for any governance activity (proposals, disputes, elections, etc.). Process types are not hard-coded; the LLM interprets your constitution to understand what types exist and how they work.
## Features ## Features
- **Pure LLM-Driven Governance**: No hard-coded governance logic - the LLM interprets the constitution and makes all decisions - **Pure LLM-Driven Governance**: No hard-coded governance logic - the LLM interprets the constitution and makes all decisions
- **Structured Memory System**: Tracks governance processes, events, and decisions in a queryable format - **Structured Memory System**: Tracks governance processes, events, and decisions in a queryable format
- **LLM Tools for Correctness**: Deterministic tools for math, dates, and random selection ensure reliability
- **Complete Auditability**: Every decision includes reasoning, constitutional citations, and calculation details - **Complete Auditability**: Every decision includes reasoning, constitutional citations, and calculation details
- **RAG-based Constitutional Reasoning**: Uses retrieval-augmented generation to understand and apply governance rules
- **Template Flexibility**: Works with diverse governance models (petition, consensus, do-ocracy, jury, circles)
- **Platform-Agnostic**: Same governance logic works across Mastodon, Discord, Telegram, Matrix, and more - **Platform-Agnostic**: Same governance logic works across Mastodon, Discord, Telegram, Matrix, and more
- **Reversible Actions**: All actions are logged and can be reversed through constitutional processes
- **Temporal Awareness**: Handles multi-day governance processes with deadlines and reminders - **Temporal Awareness**: Handles multi-day governance processes with deadlines and reminders
## Supported Platforms ## Supported Platforms
-**Mastodon** - Full implementation with streaming, admin, and moderation -**Mastodon** - Full implementation with streaming, admin, and moderation
- 🚧 **Discord** - Coming soon (see [PLATFORMS.md](PLATFORMS.md) for implementation guide) - **Slack** - Channel-scoped governance with Socket Mode
- 🚧 **Discord** - Coming soon (see [PLATFORMS.md](PLATFORMS.md))
- 🚧 **Telegram** - Coming soon - 🚧 **Telegram** - Coming soon
- 🚧 **Matrix** - Planned - 🚧 **Matrix** - Planned
Want to add a platform? See [PLATFORMS.md](PLATFORMS.md) for the implementation guide!
## Architecture
```
┌─────────────────────────────────────────┐
│ Governance Request │
│ (Natural Language from User) │
└────────────────┬────────────────────────┘
┌─────────────────────────────────────────┐
│ Governance Agent (LLM) │
│ • Interprets constitution (RAG) │
│ • Queries structured memory │
│ • Uses tools for calculations │
│ • Makes decisions with reasoning │
│ • Generates audit trails │
└────────────────┬────────────────────────┘
┌─────────┼─────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌──────┐ ┌───────┐
│ Memory │ │Tools │ │ Audit │
│ System │ │ │ │ Trail │
└────┬───┘ └──────┘ └───────┘
┌──────────────────────────────┐
│ Platform Adapter Layer │
└──────────┬───────────────────┘
┌────────┼────────┐
│ │ │
┌─▼──┐ ┌─▼──┐ ┌─▼──┐
│Mast││Disc││Tele│
│odon││ord ││gram│
└────┘ └────┘ └────┘
```
For detailed architecture documentation, see [ARCHITECTURE.md](ARCHITECTURE.md).
## Installation
```bash
# Install dependencies (using uv for faster installation)
uv pip install -e .
# For development
uv pip install -e ".[dev]"
```
## Quick Start ## Quick Start
### 1. Install Dependencies (above) ### 1. Install Dependencies
### 2. Configure (Credentials Required)
```bash ```bash
# Copy the template # Using uv (recommended, faster)
cp config/config.example.yaml config/config.yaml curl -LsSf https://astral.sh/uv/install.sh | sh
uv pip install -e .
# Edit with your credentials # Or using pip
nano config/config.yaml pip install -e .
``` ```
**⚠️ IMPORTANT**: `config/config.yaml` contains your secrets and is automatically ignored by git. Never commit this file. ### 2. Configure
```bash
cp config/config.example.yaml config/config.yaml
nano config/config.yaml # Edit with your settings
```
**⚠️ IMPORTANT**: `config/config.yaml` contains secrets and is gitignored. Never commit this file.
Configure: Configure:
- Platform credentials (Mastodon access tokens, Discord bot tokens, etc.) - **Platform credentials** (Mastodon access tokens, etc.) - See [MASTODON_SETUP.md](MASTODON_SETUP.md)
- AI model settings (Ollama local models or cloud API keys) - **AI model** (Ollama local models or cloud API keys)
- Constitution path and database location - **Constitution path** and database location
For detailed setup instructions:
- **Mastodon**: See [MASTODON_SETUP.md](MASTODON_SETUP.md)
- **Security**: See [SECURITY.md](SECURITY.md) for credential management
### 3. Set AI API Keys (if using cloud models)
For cloud models, set API keys:
```bash ```bash
# For OpenAI llm keys set openai # For OpenAI
llm keys set openai llm keys set anthropic # For Anthropic Claude
# For Anthropic Claude
llm keys set anthropic
``` ```
These are stored securely in `~/.llm/keys.json` (also gitignored) ### 3. Run the Bot
## Usage
```bash ```bash
# Run the bot # Activate virtual environment (REQUIRED)
source .venv/bin/activate
# Connect to Mastodon (or configured platform)
python -m src.govbot.bot python -m src.govbot.bot
# Test locally without Mastodon
python -m src.govbot
# Query the constitution # Query the constitution
python -m src.govbot.governance.constitution "What are the rules for proposals?" python -m src.govbot.governance.constitution "What are the rules for proposals?"
``` ```
## Testing Locally
Test all governance features without connecting to a platform:
```bash
source .venv/bin/activate # Activate virtual environment first
python -m src.govbot
```
Try these commands:
- `constitution` - View the constitution
- `query What are the rules for creating a proposal?` - Ask constitutional questions
- `propose We should update the guidelines` - Create a test proposal
- `processes` - Check active processes
- `vote 1 agree` - Vote on a process
- `status 1` - Check process status
- `actions` - View recent actions
## Architecture
The bot uses an LLM agent that:
1. Interprets natural language constitutions via RAG
2. Queries structured memory for governance state
3. Uses deterministic tools for calculations
4. Makes decisions with full reasoning trails
5. Works across any platform via adapters
```
User Request → Governance Agent (LLM) → Platform Action
Constitution (RAG)
Memory System
Audit Trail
```
For detailed architecture, see [ARCHITECTURE.md](ARCHITECTURE.md) and [ARCHITECTURE_EXAMPLE.md](ARCHITECTURE_EXAMPLE.md)
## Constitution Format ## Constitution Format
Your constitution should be a markdown file that describes: Your constitution is a markdown file describing:
- Governance processes (proposals, voting, etc.) - Governance processes (proposals, voting, etc.)
- Decision-making thresholds - Decision-making thresholds and member rights
- Member rights and responsibilities - Administrative procedures and safety mechanisms
- Administrative procedures
- Safety mechanisms (veto, appeals, etc.)
See `constitution.md` for an example based on Social.coop's bylaws. See `constitution.md` for an example based on Social.coop's bylaws.
### Publishing Constitution
The bot can publish its constitution as a **pinned Mastodon thread** with automatic version control:
```bash
# Publish or update constitution
python scripts/publish_constitution.py --summary "What changed"
```
When updated, the bot:
- Adds a deprecation notice to the previous version
- Posts the new version as a thread
- Pins it to the bot's profile
- Maintains a public history of all versions
See [CONSTITUTION_PUBLISHING.md](CONSTITUTION_PUBLISHING.md) for full documentation.
## Documentation ## Documentation
### Core Documentation **Core**
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - System architecture with LLM, memory, tools, and audit trail - [ARCHITECTURE.md](ARCHITECTURE.md) - System design and components
- **[ARCHITECTURE_EXAMPLE.md](ARCHITECTURE_EXAMPLE.md)** - Complete walkthrough of a proposal lifecycle - [ARCHITECTURE_EXAMPLE.md](ARCHITECTURE_EXAMPLE.md) - Proposal lifecycle walkthrough
- **[constitution.md](constitution.md)** - Example governance constitution - [constitution.md](constitution.md) - Example governance constitution
### Setup Guides **Setup**
- **[QUICKSTART.md](QUICKSTART.md)** - Get started quickly with CLI testing - [MASTODON_SETUP.md](MASTODON_SETUP.md) - Deploy to Mastodon
- **[MASTODON_SETUP.md](MASTODON_SETUP.md)** - Complete Mastodon deployment guide - [SLACK_SETUP.md](SLACK_SETUP.md) - Deploy to Slack
- **[PLATFORMS.md](PLATFORMS.md)** - Guide for implementing new platform adapters - [PLATFORMS.md](PLATFORMS.md) - Add new platform support
- **[SECURITY.md](SECURITY.md)** - Credential management and security best practices - [SECURITY.md](SECURITY.md) - Credential management
### Templates **Templates**
- **[templates/](templates/)** - Governance template library (petition, consensus, do-ocracy, jury, circles, dispute resolution) - [templates/](templates/) - Governance models (petition, consensus, do-ocracy, jury, circles)
## Security ## Contributing
⚠️ **Important**: Never commit `config/config.yaml` or other files containing credentials. All sensitive files are automatically protected by `.gitignore`. Early-stage project. Contributions welcome!
**See [SECURITY.md](SECURITY.md) for:** To add platform support: See [PLATFORMS.md](PLATFORMS.md)
- Complete list of protected files
- Where to store credentials
- Best practices for development and production
- What to do if secrets are accidentally committed
## Development Status
This is early-stage software. Current phase: Core infrastructure and agentic reasoning engine.
## License ## License
[To be determined] [To be determined]
## Contributing
This project is in early development. Contributions and feedback welcome!
**For platform developers**: See [PLATFORMS.md](PLATFORMS.md) to add support for Discord, Telegram, Matrix, or other platforms.

629
SLACK_SETUP.md Normal file
View File

@@ -0,0 +1,629 @@
# Slack Setup Guide
This guide walks through setting up Govbot for a Slack channel with channel-scoped governance.
## Overview
The Slack adapter enables **channel-scoped governance** where the bot manages a single channel without requiring workspace admin permissions. This makes it ideal for:
- Department or team governance within larger organizations
- Project-specific decision-making channels
- Community spaces within company Slack workspaces
- Testing governance patterns before workspace-wide deployment
## Prerequisites
- Slack workspace (free or paid tier)
- Permission to create Slack apps in the workspace
- Python 3.11+ installed
- Ollama (for local models) or API keys for cloud models
## Capabilities
The Slack adapter provides **16 governance skills** across 5 categories:
### Channel Access Control
- Invite/remove users from the governance channel
- Control who participates in governance
### Channel Management
- Create new channels for working groups
- Archive/unarchive channels
- Set channel topic and purpose
- Rename channels
### User Group Management (Channel "Roles")
- Create user groups as governance roles
- Add/remove users from groups
- Enable/disable groups
- Use groups to tag teams (e.g., @moderators, @council)
### Message Management
- Pin important governance decisions
- Unpin outdated information
### Content Moderation
- Delete messages (requires user:write scope)
## Platform Limitations
**Workspace Admin Only** (not available via channel bot):
- User account management (deactivate, invite new users)
- Workspace settings
- Emoji management
- Billing and plans
**Not Possible via API**:
- Role/permission management (owners, admins)
- Slack Connect (external organizations)
- Enterprise Grid features
- Workflow management
The bot is aware of these limitations and will explain them to users when requested.
## Step 1: Create Slack App
### 1.1 Create App
1. Go to [Slack API Dashboard](https://api.slack.com/apps)
2. Click **"Create New App"**
3. Choose **"From scratch"**
4. Enter:
- **App Name**: Govbot
- **Workspace**: Select your workspace
5. Click **"Create App"**
### 1.2 Configure Bot Token Scopes
Navigate to **OAuth & Permissions****Scopes****Bot Token Scopes**
Add these scopes:
**Essential (Required)**:
- `app_mentions:read` - Receive mentions of the bot
- `chat:write` - Post messages
- `channels:read` - View basic channel info
- `channels:manage` - Manage channels (rename, archive, etc.)
- `channels:history` - Read message history
- `users:read` - Get user information
- `usergroups:read` - View user groups
- `usergroups:write` - Create and manage user groups
- `pins:write` - Pin/unpin messages
**Optional (Enhanced Features)**:
- `channels:write` - Create new channels
- `chat:write.public` - Post to channels bot isn't in
- `im:read` - Read direct messages
- `im:write` - Send direct messages
- `im:history` - Read DM history
**Moderation (If Needed)**:
- `chat:write.customize` - Post as other users (for announcements)
- `users:write` - Modify user accounts (requires admin)
### 1.3 Enable Socket Mode
1. Navigate to **Socket Mode** in the sidebar
2. Click **"Enable Socket Mode"**
3. When prompted, create an app-level token:
- **Token Name**: govbot-socket
- **Scopes**: `connections:write`
4. **Save the app token** (starts with `xapp-`) - you'll need this
### 1.4 Enable Events
1. Navigate to **Event Subscriptions**
2. Toggle **"Enable Events"** to On
3. Under **Subscribe to bot events**, add:
- `app_mention` - When someone mentions the bot
- `message.im` - Direct messages to the bot
4. Click **"Save Changes"**
### 1.5 Install App to Workspace
1. Navigate to **Install App**
2. Click **"Install to Workspace"**
3. Review permissions and click **"Allow"**
4. **Save the Bot User OAuth Token** (starts with `xoxb-`) - you'll need this
## Step 2: Add Bot to Channel
### 2.1 Create or Select Governance Channel
1. Create a new channel (e.g., `#governance`) or select existing channel
2. Invite the bot to the channel:
```
/invite @Govbot
```
### 2.2 Get Channel ID
**Method 1: Via Channel Details**
1. Right-click the channel name
2. Click "View channel details"
3. Scroll to bottom - Channel ID is shown
4. Copy the ID (format: `C0123456789`)
**Method 2: Via URL**
- When viewing the channel, the URL contains the ID:
`https://app.slack.com/client/T.../C0123456789`
(The part after the last `/` starting with `C`)
## Step 3: Configure Govbot
### 3.1 Update Configuration File
1. **Copy the example config**:
```bash
cp config/config.example.yaml config/config.yaml
```
2. **Edit `config/config.yaml`**:
```yaml
platform:
type: slack
slack:
bot_token: xoxb-your-bot-oauth-token-here
app_token: xapp-your-app-level-token-here
channel_id: C0123456789 # Your governance channel ID
ai:
# For local models:
default_model: llama3.2
# For cloud models:
# default_model: gpt-4o-mini
# openai_api_key: your-key-here
# (or configure with: llm keys set openai)
governance:
constitution_path: constitution.md
db_path: govbot.db
default_veto_threshold: 0.67
enable_auto_execution: true
require_confirmation_for:
- admin_action
- moderation
debug: false
log_level: INFO
```
3. **Test LLM configuration**:
```bash
# Activate virtual environment
source .venv/bin/activate
# Test that llm works
llm "Hello, test message"
# If using local models, verify Ollama is running
ollama list
```
## Step 4: Initialize Database
```bash
# Activate virtual environment
source .venv/bin/activate
# Create the database
python -c "from src.govbot.db.models import init_db; init_db('govbot.db'); print('Database initialized!')"
```
## Step 5: Test with CLI
Before connecting to Slack, test the bot logic:
```bash
# Run the CLI
python -m src.govbot
# Try these commands:
query What are the rules for proposals?
propose We should have weekly team meetings
processes
exit
```
## Step 6: Install Dependencies
```bash
# Activate virtual environment
source .venv/bin/activate
# Install/update dependencies (includes slack-sdk)
uv pip install -e .
# Or: pip install -e .
```
## Step 7: Run the Bot
```bash
# Activate virtual environment
source .venv/bin/activate
# Start the bot
python -m src.govbot.bot
# You should see:
# INFO - Creating slack platform adapter...
# INFO - Connected to platform successfully
# INFO - Started listening for messages
# INFO - Bot is running. Press Ctrl+C to stop.
```
## Step 8: Test on Slack
### Basic Test
In your governance channel, type:
```
@Govbot help
```
The bot should respond with information about governance commands.
### Create a Proposal
```
@Govbot I propose we add a weekly team retrospective on Fridays
```
The bot should:
- Acknowledge the proposal
- Cite constitutional authority
- Explain voting period and threshold
- Provide voting instructions
### Vote on Proposal
In a thread reply or new message:
```
@Govbot agree
```
### Check Status
```
@Govbot status 1
```
### Test Platform Skills
```
@Govbot what actions can you perform in this channel?
```
### Create a User Group (Role)
```
@Govbot create a user group called "council" for governance leadership
```
## Platform Skills Reference
### Channel Access
- **invite_to_channel**: Add user to governance channel
- **remove_from_channel**: Remove user from channel
### Channel Management
- **create_channel**: Create new channel for working groups
- **archive_channel**: Archive a channel
- **unarchive_channel**: Restore archived channel
- **set_channel_topic**: Update channel topic
- **set_channel_purpose**: Update channel description
- **rename_channel**: Change channel name
### User Groups (Roles)
- **create_user_group**: Create governance role (e.g., @moderators)
- **add_to_user_group**: Add user to role
- **remove_from_user_group**: Remove user from role
- **disable_user_group**: Disable a role
### Message Management
- **pin_message**: Pin important decisions
- **unpin_message**: Unpin messages
- **delete_message**: Remove inappropriate content
## Troubleshooting
### Bot Not Responding
**Check**:
- Bot is invited to the channel (`/invite @Govbot`)
- Bot token (xoxb-...) is correct
- App token (xapp-...) is correct
- Socket Mode is enabled
- Events are subscribed (`app_mention`, `message.im`)
- Bot process is running (check logs)
**Test manually**:
```python
from slack_sdk import WebClient
client = WebClient(token="xoxb-your-token")
response = client.auth_test()
print(f"Bot ID: {response['user_id']}")
print(f"Bot Name: {response['user']}")
```
### Socket Mode Connection Errors
**Check**:
- App token has `connections:write` scope
- No firewall blocking WebSocket connections
- Correct app token (not bot token)
**Enable debug logging**:
```yaml
debug: true
log_level: DEBUG
```
### Permission Errors
**Common issues**:
- Missing bot token scopes (check OAuth & Permissions)
- Bot not invited to channel
- User attempting action they don't have permission for
**Verify scopes**:
```python
from slack_sdk import WebClient
client = WebClient(token="xoxb-your-token")
response = client.auth_test()
print(response) # Shows which scopes are active
```
### Channel ID Issues
**Symptoms**: Bot responds but actions fail
**Fix**: Verify channel ID
```python
from slack_sdk import WebClient
client = WebClient(token="xoxb-your-token")
response = client.conversations_list()
for channel in response['channels']:
print(f"{channel['name']}: {channel['id']}")
```
### LLM Errors
**For local models**:
```bash
# Check Ollama is running
ollama list
ollama ps
# Test the model
llm -m llama3.2 "test"
```
**For cloud models**:
```bash
# Check API keys
llm keys list
# Test the model
llm -m gpt-4o-mini "test"
```
### Database Errors
**Reset database**:
```bash
# Backup first!
cp govbot.db govbot.db.backup
# Delete and reinitialize
rm govbot.db
python -c "from src.govbot.db.models import init_db; init_db('govbot.db')"
```
## Security Considerations
**📖 See [SECURITY.md](SECURITY.md) for the complete security guide.**
### Credentials
- **Never commit** `config/config.yaml` to version control (it's in `.gitignore`)
- Store tokens securely
- Use environment variables for production:
```bash
export GOVBOT_PLATFORM__SLACK__BOT_TOKEN="xoxb-..."
export GOVBOT_PLATFORM__SLACK__APP_TOKEN="xapp-..."
export GOVBOT_PLATFORM__SLACK__CHANNEL_ID="C0123456789"
```
- Rotate tokens periodically via Slack App dashboard
- Use separate apps for development and production
### Access Control
- Limit bot scopes to minimum required
- Don't grant unnecessary permissions
- Monitor bot actions through Slack audit logs
- Review user group membership regularly
- Use channel-specific governance (not workspace-wide)
### Rate Limiting
Slack has rate limits for API calls:
- **Tier 1**: 1 request per second (most methods)
- **Tier 2**: 20 requests per minute (sending messages)
- **Tier 3**: 50 requests per minute (most read operations)
The bot handles rate limits automatically with retries, but be aware for high-volume governance.
## Production Deployment
### Systemd Service
Create `/etc/systemd/system/govbot.service`:
```ini
[Unit]
Description=Govbot Governance Bot (Slack)
After=network.target
[Service]
Type=simple
User=govbot
WorkingDirectory=/home/govbot/agentic-govbot
ExecStart=/usr/bin/python3 -m src.govbot.bot
Restart=always
RestartSec=10
Environment="GOVBOT_PLATFORM__SLACK__BOT_TOKEN=xoxb-..."
Environment="GOVBOT_PLATFORM__SLACK__APP_TOKEN=xapp-..."
Environment="GOVBOT_PLATFORM__SLACK__CHANNEL_ID=C0123456789"
Environment="GOVBOT_LOG_LEVEL=INFO"
[Install]
WantedBy=multi-user.target
```
Enable and start:
```bash
sudo systemctl enable govbot
sudo systemctl start govbot
sudo systemctl status govbot
# View logs
sudo journalctl -u govbot -f
```
### Docker Deployment
Create `Dockerfile`:
```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install -e .
CMD ["python", "-m", "src.govbot.bot"]
```
Build and run:
```bash
docker build -t govbot .
docker run \
-e GOVBOT_PLATFORM__SLACK__BOT_TOKEN=xoxb-... \
-e GOVBOT_PLATFORM__SLACK__APP_TOKEN=xapp-... \
-e GOVBOT_PLATFORM__SLACK__CHANNEL_ID=C0123456789 \
-v $(pwd)/config:/app/config \
-v $(pwd)/govbot.db:/app/govbot.db \
govbot
```
### Monitoring
- Monitor Socket Mode connection health
- Track API rate limit usage
- Log all governance actions
- Alert on connection failures
- Monitor database growth
- Track user group membership changes
## Advanced Configuration
### Multiple Channels
To manage multiple governance channels, run multiple bot instances:
```yaml
# config/governance-channel-1.yaml
platform:
type: slack
slack:
bot_token: xoxb-same-token
app_token: xapp-same-token
channel_id: C0111111111 # Different channel
governance:
db_path: govbot-channel1.db # Separate database
```
### User Groups as Roles
User groups act as channel "roles":
```
# Create governance roles
@Govbot create user group "moderators"
@Govbot create user group "council"
@Govbot create user group "working-group-a"
# Assign members
@Govbot add @alice to moderators
@Govbot add @bob to council
# Use in governance
"Only @moderators can approve reports"
"@council members have veto power"
```
### Custom Constitution
Edit `constitution.md` to define Slack-specific governance:
```markdown
## Slack Governance
### Channel Roles
- **@moderators**: Handle reports and moderation
- **@council**: Make policy decisions
- **Members**: All channel participants
### Channel Management
New channels require:
1. Proposal with clear purpose
2. 2/3 approval from @council
3. Assignment of channel owner
### User Groups
- Creation requires council approval
- Membership changes logged publicly
- Groups reviewed quarterly
```
## Next Steps
- Customize the constitution for your team/department
- Test governance workflows with your team
- Create initial user groups (roles)
- Set up announcement patterns
- Document your Slack governance process
- Gather feedback and iterate
## Getting Help
- Check [README.md](README.md) for general architecture
- Review [PLATFORMS.md](PLATFORMS.md) for platform details
- See [MASTODON_SETUP.md](MASTODON_SETUP.md) for comparison
- Review [Slack API Documentation](https://api.slack.com/docs)
- Open an issue on GitHub for bugs or questions
## Comparison: Slack vs Mastodon
| Feature | Slack | Mastodon |
|---------|-------|----------|
| **Scope** | Channel-scoped | Instance-wide |
| **Admin Powers** | Limited to channel | Full instance admin |
| **Setup** | OAuth app + tokens | OAuth + instance admin |
| **User Management** | User groups only | Full account management |
| **Moderation** | Message deletion | Suspend, silence, ban |
| **Federation** | No | Yes (block domains) |
| **Rate Limits** | Tiered, generous | Per instance config |
| **Best For** | Team/dept governance | Community instance governance |
---
**Important**: This is governance infrastructure for your team. Test thoroughly with a small group before deploying to larger channels!

View File

@@ -1,5 +1,5 @@
platform: platform:
# Platform type: mastodon, discord, telegram, mock # Platform type: mastodon, slack, discord, telegram, mock
type: mastodon type: mastodon
# Mastodon configuration (if using Mastodon) # Mastodon configuration (if using Mastodon)
@@ -10,6 +10,12 @@ platform:
access_token: your_access_token_here access_token: your_access_token_here
bot_username: govbot bot_username: govbot
# Slack configuration (if using Slack)
# slack:
# bot_token: xoxb-your-bot-oauth-token-here
# app_token: xapp-your-app-level-token-here
# channel_id: C0123456789
# Discord configuration (for future use) # Discord configuration (for future use)
# discord: # discord:
# token: your_discord_bot_token # token: your_discord_bot_token

View File

@@ -6,6 +6,7 @@ readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [
"mastodon.py>=1.8.0", "mastodon.py>=1.8.0",
"slack-sdk>=3.33.0",
"sqlalchemy>=2.0.0", "sqlalchemy>=2.0.0",
"pydantic>=2.0.0", "pydantic>=2.0.0",
"pydantic-settings>=2.0.0", "pydantic-settings>=2.0.0",

View File

@@ -1,5 +1,6 @@
# Core dependencies # Core dependencies
Mastodon.py>=1.8.0 Mastodon.py>=1.8.0
slack-sdk>=3.33.0
SQLAlchemy>=2.0.0 SQLAlchemy>=2.0.0
llm>=0.13.0 llm>=0.13.0
llm-anthropic>=0.23 llm-anthropic>=0.23

104
scripts/publish_constitution.py Executable file
View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""
Helper script to publish constitution to Mastodon.
Usage:
python scripts/publish_constitution.py [--summary "What changed"]
"""
import sys
import argparse
from pathlib import Path
# Add src to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.govbot.utils.config import load_config
from src.govbot.platforms.mastodon import MastodonAdapter
def main():
parser = argparse.ArgumentParser(description="Publish constitution to Mastodon")
parser.add_argument(
"--summary",
"-s",
default="Constitution updated",
help="Summary of changes made"
)
parser.add_argument(
"--constitution",
"-c",
default="constitution.md",
help="Path to constitution file (default: constitution.md)"
)
parser.add_argument(
"--config",
default="config/config.yaml",
help="Path to config file (default: config/config.yaml)"
)
args = parser.parse_args()
# Load constitution
constitution_path = Path(args.constitution)
if not constitution_path.exists():
print(f"❌ Constitution file not found: {constitution_path}")
sys.exit(1)
constitution_text = constitution_path.read_text()
print(f"📜 Loaded constitution from {constitution_path} ({len(constitution_text)} chars)")
# Load config
try:
config = load_config(args.config)
except FileNotFoundError:
print(f"❌ Config file not found: {args.config}")
print(" Copy config/config.example.yaml to config/config.yaml and configure")
sys.exit(1)
# Connect to Mastodon
print(f"🔌 Connecting to {config.platform.mastodon.instance_url}...")
adapter = MastodonAdapter(config.platform.mastodon.model_dump())
if not adapter.connect():
print("❌ Failed to connect to Mastodon")
sys.exit(1)
print(f"✅ Connected as @{adapter.bot_username}")
# Publish constitution
print(f"\n📤 Publishing constitution...")
print(f" Change summary: {args.summary}")
result = adapter.execute_skill(
skill_name="publish_constitution",
parameters={
"constitution_text": constitution_text,
"change_summary": args.summary,
},
actor="@admin"
)
if result["success"]:
print(f"\n{result['message']}")
print(f"\n📊 Details:")
print(f" - Thread length: {result['data']['thread_length']} posts")
print(f" - First post ID: {result['data']['first_post_id']}")
if result['data'].get('previous_post_id'):
print(f" - Previous post ID: {result['data']['previous_post_id']} (deprecated)")
# Generate profile URL
profile_url = f"{adapter.instance_url}/@{adapter.bot_username}"
print(f"\n🔗 View at: {profile_url}")
else:
print(f"\n❌ Failed: {result['message']}")
sys.exit(1)
# Disconnect
adapter.disconnect()
print("\n✨ Done!")
if __name__ == "__main__":
main()

View File

@@ -61,7 +61,8 @@ class GovernanceAgent:
request: str, request: str,
actor: str, actor: str,
context: Optional[Dict[str, Any]] = None, context: Optional[Dict[str, Any]] = None,
platform_skills: Optional[List[Dict[str, Any]]] = None platform_skills: Optional[List[Dict[str, Any]]] = None,
platform_limitations: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Process a governance request using agentic interpretation. Process a governance request using agentic interpretation.
@@ -80,6 +81,7 @@ class GovernanceAgent:
actor: Who made the request actor: Who made the request
context: Optional context (thread ID, etc.) context: Optional context (thread ID, etc.)
platform_skills: List of available platform-specific skills platform_skills: List of available platform-specific skills
platform_limitations: Information about what's not possible via API
Returns: Returns:
Response dictionary with action taken and audit trail Response dictionary with action taken and audit trail
@@ -106,7 +108,8 @@ class GovernanceAgent:
memory=memory_context, memory=memory_context,
actor=actor, actor=actor,
context=context, context=context,
platform_skills=platform_skills platform_skills=platform_skills,
platform_limitations=platform_limitations
) )
# Step 5: Execute the decision # Step 5: Execute the decision
@@ -225,7 +228,8 @@ Return your analysis as JSON:
memory: Dict[str, Any], memory: Dict[str, Any],
actor: str, actor: str,
context: Optional[Dict[str, Any]], context: Optional[Dict[str, Any]],
platform_skills: Optional[List[Dict[str, Any]]] = None platform_skills: Optional[List[Dict[str, Any]]] = None,
platform_limitations: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Use LLM to decide what action to take. Use LLM to decide what action to take.
@@ -247,10 +251,34 @@ Return your analysis as JSON:
platform_skills_info = "\n\nAVAILABLE PLATFORM SKILLS:\n" platform_skills_info = "\n\nAVAILABLE PLATFORM SKILLS:\n"
for skill in platform_skills: for skill in platform_skills:
params_str = ", ".join([f"{p['name']}: {p['type']}" for p in skill.get('parameters', [])]) params_str = ", ".join([f"{p['name']}: {p['type']}" for p in skill.get('parameters', [])])
category = skill.get('category', 'general')
platform_skills_info += f"- {skill['name']}({params_str}): {skill['description']}\n" platform_skills_info += f"- {skill['name']}({params_str}): {skill['description']}\n"
platform_skills_info += f" Category: {category}\n"
if skill.get('constitutional_authorization'): if skill.get('constitutional_authorization'):
platform_skills_info += f" Authorization: {skill['constitutional_authorization']}\n" platform_skills_info += f" Authorization: {skill['constitutional_authorization']}\n"
# Format platform limitations for the prompt
platform_limitations_info = ""
if platform_limitations:
platform_limitations_info = "\n\nPLATFORM LIMITATIONS:\n"
if 'web_interface_only' in platform_limitations:
platform_limitations_info += "\nActions requiring web interface (NOT available via bot):\n"
for key, desc in platform_limitations['web_interface_only'].items():
platform_limitations_info += f"- {key}: {desc}\n"
if 'not_possible' in platform_limitations:
platform_limitations_info += "\nActions not possible via any API:\n"
for key, desc in platform_limitations['not_possible'].items():
platform_limitations_info += f"- {key}: {desc}\n"
if 'limitations' in platform_limitations:
platform_limitations_info += "\nGeneral limitations to be aware of:\n"
for key, desc in platform_limitations['limitations'].items():
platform_limitations_info += f"- {key}: {desc}\n"
platform_limitations_info += "\nIMPORTANT: If a user requests something that requires web interface or is not possible via API, explain this limitation clearly and provide the web interface URL if applicable."
prompt = f"""You are a governance bot interpreting a community constitution. prompt = f"""You are a governance bot interpreting a community constitution.
IMPORTANT: When generating the "response" field, use newline characters (\\n) for line breaks: IMPORTANT: When generating the "response" field, use newline characters (\\n) for line breaks:
@@ -269,7 +297,7 @@ CURRENT MEMORY STATE:
{json.dumps(memory, indent=2)} {json.dumps(memory, indent=2)}
ACTOR: {actor} ACTOR: {actor}
{platform_skills_info} {platform_skills_info}{platform_limitations_info}
Based on the constitution and current state, decide what action to take. Based on the constitution and current state, decide what action to take.
IMPORTANT AUTHORITY CHECK: IMPORTANT AUTHORITY CHECK:

View File

@@ -25,6 +25,7 @@ from .agent import GovernanceAgent
from .scheduler import GovernanceScheduler from .scheduler import GovernanceScheduler
from .platforms.base import PlatformAdapter, PlatformMessage, MockPlatformAdapter from .platforms.base import PlatformAdapter, PlatformMessage, MockPlatformAdapter
from .platforms.mastodon import MastodonAdapter from .platforms.mastodon import MastodonAdapter
from .platforms.slack import SlackAdapter
# Configure logging # Configure logging
@@ -125,12 +126,14 @@ class Govbot:
if platform_type == "mastodon": if platform_type == "mastodon":
return MastodonAdapter(self.config.platform.mastodon.model_dump()) return MastodonAdapter(self.config.platform.mastodon.model_dump())
elif platform_type == "slack":
return SlackAdapter(self.config.platform.slack.model_dump())
elif platform_type == "mock": elif platform_type == "mock":
return MockPlatformAdapter({}) return MockPlatformAdapter({})
else: else:
raise ValueError( raise ValueError(
f"Unknown platform type: {platform_type}. " f"Unknown platform type: {platform_type}. "
f"Supported: mastodon, mock" f"Supported: mastodon, slack, mock"
) )
def run(self): def run(self):
@@ -199,6 +202,7 @@ class Govbot:
# Get available platform skills # Get available platform skills
platform_skills_list = [] platform_skills_list = []
platform_limitations = None
try: try:
platform_skills = self.platform.get_skills() platform_skills = self.platform.get_skills()
# Convert PlatformSkill objects to dicts for the agent # Convert PlatformSkill objects to dicts for the agent
@@ -214,6 +218,10 @@ class Govbot:
"constitutional_authorization": skill.constitutional_authorization, "constitutional_authorization": skill.constitutional_authorization,
} }
platform_skills_list.append(skill_dict) platform_skills_list.append(skill_dict)
# Get platform limitations if available
if hasattr(self.platform, 'get_platform_limitations'):
platform_limitations = self.platform.get_platform_limitations()
except Exception as e: except Exception as e:
logger.warning(f"Could not get platform skills: {e}") logger.warning(f"Could not get platform skills: {e}")
@@ -222,6 +230,7 @@ class Govbot:
actor=f"@{message.author_handle}", actor=f"@{message.author_handle}",
context=context, context=context,
platform_skills=platform_skills_list if platform_skills_list else None, platform_skills=platform_skills_list if platform_skills_list else None,
platform_limitations=platform_limitations,
) )
# Check if we need to execute a platform skill # Check if we need to execute a platform skill

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,15 @@ class MastodonConfig(BaseModel):
bot_username: str = Field("govbot", description="Bot's Mastodon username") bot_username: str = Field("govbot", description="Bot's Mastodon username")
class SlackConfig(BaseModel):
"""Slack workspace configuration"""
bot_token: str = Field(..., description="Slack bot user OAuth token (xoxb-...)")
app_token: str = Field(..., description="Slack app-level token for Socket Mode (xapp-...)")
channel_id: str = Field(..., description="Channel ID for governance (e.g., C0123456789)")
bot_user_id: Optional[str] = Field(None, description="Bot user ID (will be auto-detected)")
class AIConfig(BaseModel): class AIConfig(BaseModel):
"""AI model configuration""" """AI model configuration"""
@@ -58,8 +67,9 @@ class GovernanceConfig(BaseModel):
class PlatformConfig(BaseModel): class PlatformConfig(BaseModel):
"""Platform selection and configuration""" """Platform selection and configuration"""
type: str = Field(..., description="Platform type: mastodon, discord, telegram, mock") type: str = Field(..., description="Platform type: mastodon, slack, discord, telegram, mock")
mastodon: Optional[MastodonConfig] = Field(None, description="Mastodon configuration") mastodon: Optional[MastodonConfig] = Field(None, description="Mastodon configuration")
slack: Optional[SlackConfig] = Field(None, description="Slack configuration")
# Future platforms: # Future platforms:
# discord: Optional[DiscordConfig] = None # discord: Optional[DiscordConfig] = None
# telegram: Optional[TelegramConfig] = None # telegram: Optional[TelegramConfig] = None
@@ -117,7 +127,7 @@ def create_example_config(output_path: str = "config/config.example.yaml"):
""" """
example_config = { example_config = {
"platform": { "platform": {
"type": "mastodon", # or "discord", "telegram", "mock" "type": "mastodon", # or "slack", "discord", "telegram", "mock"
"mastodon": { "mastodon": {
"instance_url": "https://your-mastodon-instance.social", "instance_url": "https://your-mastodon-instance.social",
"client_id": "your_client_id_here", "client_id": "your_client_id_here",
@@ -125,6 +135,12 @@ def create_example_config(output_path: str = "config/config.example.yaml"):
"access_token": "your_access_token_here", "access_token": "your_access_token_here",
"bot_username": "govbot", "bot_username": "govbot",
}, },
# Slack example:
# "slack": {
# "bot_token": "xoxb-your-bot-token-here",
# "app_token": "xapp-your-app-token-here",
# "channel_id": "C0123456789",
# },
# Discord example (for future use): # Discord example (for future use):
# "discord": { # "discord": {
# "token": "your_discord_bot_token", # "token": "your_discord_bot_token",