|
| 1 | +# Plugin ActivityPub |
| 2 | + |
| 3 | +Expõe cada **Agente** do Mapas Culturais como um **Actor ActivityPub**, permitindo que instâncias do Fediverso (Mastodon, Pleroma, Misskey etc.) descubram e sigam agentes culturais via protocolo W3C ActivityPub. |
| 4 | + |
| 5 | +## Como funciona |
| 6 | + |
| 7 | +### Descoberta via WebFinger |
| 8 | + |
| 9 | +O Fediverso descobre atores pelo protocolo [WebFinger (RFC 7033)](https://www.rfc-editor.org/rfc/rfc7033). Para buscar um agente chamado **Admin**: |
| 10 | + |
| 11 | +``` |
| 12 | +GET /.well-known/webfinger?resource=acct:admin@seu.dominio |
| 13 | +``` |
| 14 | + |
| 15 | +Resposta: |
| 16 | +```json |
| 17 | +{ |
| 18 | + "subject": "acct:admin@seu.dominio", |
| 19 | + "links": [{ |
| 20 | + "rel": "self", |
| 21 | + "type": "application/activity+json", |
| 22 | + "href": "https://seu.dominio/activitypub/agent/admin" |
| 23 | + }] |
| 24 | +} |
| 25 | +``` |
| 26 | + |
| 27 | +O username (`admin`) é gerado automaticamente a partir do nome do agente via slugify — acentos são transliterados, espaços viram hífens. |
| 28 | + |
| 29 | +### Perfil do Actor (Person) |
| 30 | + |
| 31 | +``` |
| 32 | +GET /activitypub/agent/{slug} |
| 33 | +Accept: application/activity+json |
| 34 | +``` |
| 35 | + |
| 36 | +Retorna um objeto `Person` com: |
| 37 | +- `id`, `preferredUsername`, `name`, `summary`, `url` |
| 38 | +- `inbox` e `outbox` |
| 39 | +- `publicKey` (stub vazio — assinatura HTTP não implementada ainda) |
| 40 | +- `icon` (avatar do agente, se disponível) |
| 41 | + |
| 42 | +### Outbox paginada |
| 43 | + |
| 44 | +``` |
| 45 | +GET /activitypub/agent/{slug}/outbox # coleção total |
| 46 | +GET /activitypub/agent/{slug}/outbox?page=1 # primeira página (20 itens) |
| 47 | +``` |
| 48 | + |
| 49 | +Retorna `OrderedCollection` / `OrderedCollectionPage` com as atividades registradas do agente. As atividades são gravadas na tabela `activitypub_activity` via job assíncrono quando o agente salva eventos, espaços, projetos, oportunidades ou inscrições. |
| 50 | + |
| 51 | +### Atividades suportadas |
| 52 | + |
| 53 | +| Entidade Mapas | Tipo ActivityPub | Disparado em | |
| 54 | +|----------------------|------------------|--------------------| |
| 55 | +| Event | `Event` | Create / Update | |
| 56 | +| Space | `Place` | Create / Update | |
| 57 | +| Project | `Note` | Create / Update | |
| 58 | +| Opportunity | `Note` | Create / Update | |
| 59 | +| Registration | `Note` | Announce | |
| 60 | +| AgentRelation | `Relationship` | Add | |
| 61 | + |
| 62 | +## Endpoints |
| 63 | + |
| 64 | +| Método | Rota | Descrição | |
| 65 | +|--------|---------------------------------------------------|-------------------------| |
| 66 | +| GET | `/.well-known/webfinger?resource=acct:{slug}@{domain}` | Descoberta WebFinger | |
| 67 | +| GET | `/activitypub/agent/{slug}` | Perfil Actor (Person) | |
| 68 | +| GET | `/activitypub/agent/{slug}/outbox[?page=N]` | Outbox paginada | |
| 69 | +| GET | `/activitypub/agent/{slug}/inbox` | Inbox (stub vazio) | |
| 70 | +| GET | `/activitypub/agent/{slug}/activities/{hash}` | Atividade individual | |
| 71 | + |
| 72 | +O `{slug}` aceita tanto o nome slugificado (`admin`) quanto o ID numérico do agente (`42`) para compatibilidade. |
| 73 | + |
| 74 | +## Configuração |
| 75 | + |
| 76 | +Em `dev/config.d/0.main.php` (ou equivalente de produção): |
| 77 | + |
| 78 | +```php |
| 79 | +'activitypub.enabled' => true, |
| 80 | +'activitypub.domain' => '', // deixar vazio para usar o host de base.url |
| 81 | +``` |
| 82 | + |
| 83 | +Em `dev/config.d/plugins.php`: |
| 84 | + |
| 85 | +```php |
| 86 | +'ActivityPub' => ['namespace' => 'ActivityPub'], |
| 87 | +``` |
| 88 | + |
| 89 | +## Banco de dados |
| 90 | + |
| 91 | +A migration cria a tabela `activitypub_activity` automaticamente na inicialização do container: |
| 92 | + |
| 93 | +```sql |
| 94 | +id BIGSERIAL PRIMARY KEY |
| 95 | +agent_id INTEGER REFERENCES agent(id) ON DELETE CASCADE |
| 96 | +type VARCHAR(50) -- Create, Update, Announce, Add |
| 97 | +object_type VARCHAR(100) -- MapasCulturais\Entities\Event etc. |
| 98 | +object_id INTEGER |
| 99 | +activity_id TEXT UNIQUE -- URI canônica da atividade |
| 100 | +payload JSONB -- payload completo ActivityPub |
| 101 | +published TIMESTAMPTZ |
| 102 | +``` |
| 103 | + |
| 104 | +Índices: performance em `(agent_id, published DESC)` e deduplicação parcial em `(agent_id, object_type, object_id) WHERE type = 'Create'`. |
| 105 | + |
| 106 | +## Testando localmente |
| 107 | + |
| 108 | +```bash |
| 109 | +# 1. Copiar plugin (container monta do repo principal) |
| 110 | +cp -r src/plugins/ActivityPub /var/www/src/plugins/ # ou via bind mount |
| 111 | + |
| 112 | +# 2. Aplicar migration |
| 113 | +./scripts/db-update.sh |
| 114 | + |
| 115 | +# 3. Descobrir um agente (substitua "admin" pelo slug do agente) |
| 116 | +curl -s "http://localhost:8080/.well-known/webfinger?resource=acct:admin@localhost" | jq . |
| 117 | + |
| 118 | +# 4. Ver perfil Actor |
| 119 | +curl -s -H "Accept: application/activity+json" \ |
| 120 | + "http://localhost:8080/activitypub/agent/admin" | jq . |
| 121 | + |
| 122 | +# 5. Ver outbox |
| 123 | +curl -s -H "Accept: application/activity+json" \ |
| 124 | + "http://localhost:8080/activitypub/agent/admin/outbox" | jq . |
| 125 | +``` |
| 126 | + |
| 127 | +Para descobrir slugs disponíveis: |
| 128 | +```bash |
| 129 | +dev/psql.sh -c "SELECT id, name FROM agent WHERE status = 1 LIMIT 10;" |
| 130 | +``` |
| 131 | + |
| 132 | +## Limitações atuais |
| 133 | + |
| 134 | +- **Sem assinatura HTTP**: o `publicKey.publicKeyPem` é vazio — outros servidores ActivityPub não conseguem verificar requisições de saída. Necessário para follow/unfollow real. |
| 135 | +- **Sem Inbox funcional**: requisições recebidas de outros servidores são ignoradas. |
| 136 | +- **Slug não único**: dois agentes com o mesmo nome geram o mesmo slug; o primeiro ativo encontrado é retornado. |
| 137 | +- **Busca por slug em memória**: `findAgent()` itera todos os agentes ativos. Adequado para instâncias pequenas; instâncias grandes precisarão de coluna `activitypub_username` indexada. |
0 commit comments