docs: publish governance, governed boosts, receipts, OIDC phase 2, files SSO

Engineering guide: session/refresh/logout architecture, publish-governance
and amplify gating sections, file map to migrations 001-034. Admin guide:
governance administration, files-SSO operations, verification harnesses,
migration list + deploy order + 034 session note. User manual: calendar
receipt badges, boost gating, connections governance note (replaces the
stale 'stub' statuses). Design doc 2026-10-13: marked implemented with the
as-shipped phase 2. Wiki (EN/ES): Drive sign-in + boosts/approvals.
README: governance + Files SSO bullets.
This commit is contained in:
glass beadian 2026-10-14 11:52:48 +00:00
parent 643fbb94bc
commit b8671113f5
7 changed files with 113 additions and 21 deletions

View file

@ -10,7 +10,8 @@ Every feature is multi-tenant with row-level security.
## Features ## Features
- **Publishing pipeline** — drafts → group approval → schedule → fenced dispatch → receipts. Multi-platform fan-out (Mastodon, Telegram, Bluesky, PeerTube, Facebook, Instagram, Threads, LinkedIn, Reddit, Twitch, Discord, Slack). DST-proof scheduling. - **Publishing pipeline** — drafts → group approval → schedule → fenced dispatch → receipts. Per-connection publish governance: `auto`, one-admin, majority or consensus sign-off per account — boosts included. Multi-platform fan-out (Mastodon, Telegram, Bluesky, PeerTube, Facebook, Instagram, Threads, LinkedIn, Reddit, Twitch, Discord, Slack). DST-proof scheduling.
- **Files SSO** — the Hub is the OIDC issuer for oCIS Drive: one sign-in, auto-provisioned personal drives, rotating refresh tokens that die with your Hub session (logout propagates).
- **Column wall** — live multi-platform feed columns, TweetDeck-style. Safe rendering (hostile-HTML tested). Per-source cooldown + adaptive backoff. - **Column wall** — live multi-platform feed columns, TweetDeck-style. Safe rendering (hostile-HTML tested). Per-source cooldown + adaptive backoff.
- **Live Control Room** — browser multiview, TAKE/CUT, preview/program, lower-thirds, scrolling chyrons, audio mixing, tally. - **Live Control Room** — browser multiview, TAKE/CUT, preview/program, lower-thirds, scrolling chyrons, audio mixing, tally.
- **Auto-Director** — priority rules switch sources automatically so the stream never dies mid-action. - **Auto-Director** — priority rules switch sources automatically so the stream never dies mid-action.

View file

@ -68,7 +68,7 @@ openssl rand -base64 32 # → SESSION_SECRET
- `sp_app` — created by migration `003_app_role.sql`; `NOSUPERUSER`, grants on all tables/sequences. **All runtime connections.** `FORCE ROW LEVEL SECURITY` means even the table owner cannot silently bypass tenancy. - `sp_app` — created by migration `003_app_role.sql`; `NOSUPERUSER`, grants on all tables/sequences. **All runtime connections.** `FORCE ROW LEVEL SECURITY` means even the table owner cannot silently bypass tenancy.
### 4.2 Migrations ### 4.2 Migrations
Ordered SQL files in `apps/api/migrations/` (001 tenants + RLS + audit chain · 002 RBAC seed · 003 sp_app role · 004 NULLIF policy fix · 005 epoch trigger fix · 006 publishing). Idempotent runner (`apps/api/src/db/migrate.ts`) tracks `schema_migrations`: Ordered SQL files in `apps/api/migrations/` — currently **001–034** (001 tenants + RLS + audit chain · 002 RBAC seed · 003 sp_app role · 004 NULLIF policy fix · 005 epoch trigger fix · 006 publishing · 019 publishing integration · 024 chatgov · 029 group files · 031 personal workspaces · 033 publish governance · 034 auth sessions + OIDC refresh tokens). Idempotent runner (`apps/api/src/db/migrate.ts`) tracks `schema_migrations`:
```bash ```bash
cd apps/api && SUPER_DATABASE_URL='postgresql://tmbh:…@localhost:5433/tmbh' \ cd apps/api && SUPER_DATABASE_URL='postgresql://tmbh:…@localhost:5433/tmbh' \
@ -95,6 +95,43 @@ Truth model: raising a value **does not bypass the provider** — it declares wh
37 permissions across 10 seeded roles (Owner, Admin, Publisher, Editor, Approver, Analyst, Moderator, VideoPublisher, StreamOperator, BroadcastOperator). Role changes bump the workspace `authz_epoch`; affected live sessions fail closed on their next request. Audit every membership change (hash-chained; `audit_verify_chain()` detects tampering). 37 permissions across 10 seeded roles (Owner, Admin, Publisher, Editor, Approver, Analyst, Moderator, VideoPublisher, StreamOperator, BroadcastOperator). Role changes bump the workspace `authz_epoch`; affected live sessions fail closed on their next request. Audit every membership change (hash-chained; `audit_verify_chain()` detects tampering).
## 6.1 Publish governance (who signs off a post)
Each social connection carries a publish-governance setting. Manage per connection (requires `chatgov.manage`):
```bash
curl -X PATCH https://api.occupymedia.cc/connections/$CONN/publish-governance \
-H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"publishGovernance": "admin1"}' # auto | admin1 | admin_majority | admin_consensus | null
```
| Mode | Who signs off the publish |
|---|---|
| `null` (unset) | manual: anyone with `publication.publish` |
| `auto` | nobody — dispatch fires when the draft's approval lands |
| `admin1` | one admin, via a publish poll |
| `admin_majority` | majority of admins (snapshot at poll open) |
| `admin_consensus` | every admin; one veto blocks at once |
While held, the publish/boost route answers `202 awaiting_publish_approval` with the poll id; the poll closes after a day and rejects unanswered. Reconnecting an account keeps the setting. Changing the setting does not cancel an already-open poll. The Mastodon **boost** route runs behind the same gate.
## 6.2 Files SSO (Hub as the OIDC issuer for oCIS)
`files.occupymedia.cc` signs in through the Hub: the issuer is `https://api.occupymedia.cc/oidc`, client id `web`, PKCE only, auto-consent for that first-party client. Phase 2 (migration 034) makes sessions revocable and adds rotating refresh tokens bound to the Hub session — Hub logout reaches Drive within one access-token TTL (≤1 h). Operational notes:
- `OIDC_KEY_DIR` must be a persistent volume (the API generates and seals its ES256 keyset there on first boot; losing it logs every Drive session out).
- `FILES_OCID_USER` and `OIDC_RESERVED_USERNAMES` are never claimable as files accounts (`admin` is reserved by default).
- Sign-in requires an **active membership** and a username usable as an account name; otherwise the consent endpoint fails closed.
## 6.3 Verification harnesses
```bash
RADAR_EMAIL=… RADAR_PASSWORD=… node scripts/e2e/governance-sim.mjs # S1–S9 governance scenarios, live
node scripts/e2e/brand-palette-check.mjs # fails on blue-family pixels in embedded surfaces
```
The governance sim is re-runnable (all fixtures prefixed `e2e-gov-`) and writes a report to `/tmp/opencode/workers/GOV-SIM-REPORT.md` by default.
## 7. Monitoring & health ## 7. Monitoring & health
- `GET /healthz` — liveness (no dependencies). - `GET /healthz` — liveness (no dependencies).
@ -128,6 +165,8 @@ Truth model: raising a value **does not bypass the provider** — it declares wh
1. Back up DB + confirm KEK backups (§4.3). 1. Back up DB + confirm KEK backups (§4.3).
2. `git pull && pnpm install`. 2. `git pull && pnpm install`.
3. Run migrations (§4.2) — additive by design; expand/contract policy documented per migration. 3. Run migrations (§4.2) — additive by design; expand/contract policy documented per migration. **Order matters**: 033 must exist before the scheduler that queries its columns rolls, or the E3 pre-dispatch check fails open.
4. Restart API, then scheduler. 4. Restart API, then scheduler.
5. Watch `/readyz` and the audit chain verification. 5. Watch `/readyz` and the audit chain verification.
**Session note (migration 034+):** tokens issued before 034 carry no `sid`; they keep working until expiry but `reissue` refuses them — after upgrading, users re-login at their next epoch bump or within 12 h at the latest. Logout only revokes server-side for tokens issued after the upgrade.

View file

@ -1,6 +1,7 @@
# Files SSO design — Hub as the OIDC issuer for oCIS # Files SSO design — Hub as the OIDC issuer for oCIS
Status: proposed (design pass; not implemented) Status: **implemented** (phase 1: provider + sign-in, shipped; phase 2: session-bound
refresh tokens + logout propagation, shipped — see §Phases update below)
Date: 2026-10-13 Date: 2026-10-13
Related: docs/design/2026-10-12-collectives-architecture.md, brand review P0 #2 Related: docs/design/2026-10-12-collectives-architecture.md, brand review P0 #2
(files.occupymedia.cc navy login wall), matrix-sso module (chat SSO, shipped). (files.occupymedia.cc navy login wall), matrix-sso module (chat SSO, shipped).
@ -67,12 +68,31 @@ already holds the session, the RBAC roles, and the username namespace.
## Phases ## Phases
1. **Provider + sign-in** (≈1–2 days): the module above; Drive view switches 1. **Provider + sign-in** (≈1–2 days): the module above; Drive view switches
`authMode` to `"oidc"`; the navy wall disappears. `authMode` to `"oidc"`; the navy wall disappears. **Shipped.**
2. **Personal drives** (≈1 day): uploaded files land in the user's own drive; 2. **Personal drives** (≈1 day): uploaded files land in the user's own drive;
the Library keeps serving collective folders from the service account. the Library keeps serving collective folders from the service account. **Shipped** (autoprovisioned personal spaces verified live).
3. **Share-through** (≈1 day, optional): group folders shared into members' 3. **Share-through** (≈1 day, optional): group folders shared into members'
drives so the Drive tab also shows collective Library content; only then drives so the Drive tab also shows collective Library content; only then
consider deprecating the Library tab. consider deprecating the Library tab. *(open)*
## Phase 2 as shipped (refresh + logout, migration 034)
The security note below said logout propagation was a nice-to-have; it shipped
as part of hardening instead:
- `auth_sessions` rows (`sid` in the session payload): login/reissue write the
row, `POST /auth/logout` revokes it, `reissue` refuses revoked or pre-034
tokens. Request validation stays stateless.
- `oidc_refresh_tokens`: opaque tokens, SHA-256 at rest, rotate-on-use,
`family_id` chains with reuse detection (presenting a consumed token revokes
the chain). Every refresh re-checks the session row and active membership —
so an oCIS session can never outlive the Hub sign-in that minted it, and
logout propagates within one access-token TTL (≤1 h).
- The token endpoint is unauthenticated: lookups go through the definer-rights
`oidc_refresh_lookup()` (audit_append pattern). Refresh TTL has no constant —
it *is* the Hub session's remaining lifetime.
- Matrix SSO stays out of propagation scope by design: the broker mints
2-minute `m.login.token`s and Element holds a Matrix-native session after.
## Security notes ## Security notes

View file

@ -60,8 +60,10 @@ Cross-tenant functions take an explicit `p_workspace_id` and call `fn_assert_ten
### 3.4 Continuous authorization (audit finding #2) ### 3.4 Continuous authorization (audit finding #2)
Any membership insert/update/delete fires `bump_authz_epoch()` — `COALESCE(NEW.workspace_id, OLD.workspace_id)` (OLD is NULL on INSERT; the original bug silently no-oped). Sessions embed the epoch at issue; `RbacGuard` compares against the live `workspaces.authz_epoch` and fails closed (`401, epoch stale`) on mismatch. Revocation propagates to live sessions without waiting for re-login. Any membership insert/update/delete fires `bump_authz_epoch()` — `COALESCE(NEW.workspace_id, OLD.workspace_id)` (OLD is NULL on INSERT; the original bug silently no-oped). Sessions embed the epoch at issue; `RbacGuard` compares against the live `workspaces.authz_epoch` and fails closed (`401, epoch stale`) on mismatch. Revocation propagates to live sessions without waiting for re-login.
### 3.5 Sessions & OIDC ### 3.5 Sessions, logout & the Hub as OIDC issuer
HMAC-SHA256 signed tokens (`apps/api/src/auth/session.ts`), 12h TTL, constant-time verify. OIDC: generic code flow + PKCE S256, discovery-document driven (`auth/oidc.ts`) — Authentik/Keycloak/any compliant issuer via `OIDC_*` env. HMAC-SHA256 signed tokens (`apps/api/src/auth/session.ts`), 12h TTL, constant-time verify. Since migration `034`, every session also carries a `sid` backed by an `auth_sessions` row: login/reissue write the row, `POST /auth/logout` revokes it, and `reissue` refuses both revoked rows and pre-`034` tokens (no `sid`) so revocation applies from the next epoch bump. Request validation (`RbacGuard`) stays stateless — rows are consulted only by reissue and the OIDC refresh path.
The Hub is also its own OpenID Connect **provider** for oCIS (`apps/api/src/oidc/`, issuer `https://api.occupymedia.cc/oidc`): discovery, JWKS (ES256, keys sealed via the vault pattern), `/oidc/authorize` → SPA consent → single-use code (60 s, PKCE S256 enforced) → tokens. Only the first-party client `web` (the oCIS web app) is registered; anything else fails closed. Phase 2 added **session-bound rotating refresh tokens** (`oidc_refresh_tokens`, SHA-256 at rest, `family_id` chains): a chain lives and dies with the consenting Hub session, reuse of a consumed token revokes the whole family (OAuth BCP), and every accept re-checks that the session row is live and the membership active — so Hub logout propagates to oCIS at the next refresh instead of at the next silent authorize. The unauthenticated token endpoint resolves tokens through the definer-rights `oidc_refresh_lookup()` (the `audit_append` pattern). Matrix SSO (`apps/api/src/matrix-sso/`) is a separate 2-minute `m.login.token` broker and intentionally out of propagation scope: Element holds its own Matrix-native session after mint.
### 3.6 Credential vault ### 3.6 Credential vault
Envelope encryption (`vault/vault.ts`): per-workspace DEK (AES-256-GCM) wrapped by master KEK (`SUPERPOSTER_KEK`, 32B base64; production-required). Rotation re-wraps; old DEK dropped → pre-rotation ciphertext unreadable (tested). Connectors receive opaque `CredentialRef` handles via `CredentialAccessor` — never ciphertext, never raw tokens. Envelope encryption (`vault/vault.ts`): per-workspace DEK (AES-256-GCM) wrapped by master KEK (`SUPERPOSTER_KEK`, 32B base64; production-required). Rotation re-wraps; old DEK dropped → pre-rotation ciphertext unreadable (tested). Connectors receive opaque `CredentialRef` handles via `CredentialAccessor` — never ciphertext, never raw tokens.
@ -105,6 +107,23 @@ Hash-chained per branch (`audit_append` for tenant rows, `audit_append_system` f
- **State machine** (`publishing.service.ts`): `editing → in_review → approved → scheduled → published` (+ `rejected`, `cancelled`). Every transition permission-checked (`approval.request` / `approval.grant` / `publication.schedule`) and audited. - **State machine** (`publishing.service.ts`): `editing → in_review → approved → scheduled → published` (+ `rejected`, `cancelled`). Every transition permission-checked (`approval.request` / `approval.grant` / `publication.schedule`) and audited.
- **Durability** (`006_publishing.sql`): `scheduled_publications` carries a `UNIQUE idempotency_key`; the DB row commits first; if Temporal is unreachable the row stays `pending` and the worker's startup sweep picks it up. - **Durability** (`006_publishing.sql`): `scheduled_publications` carries a `UNIQUE idempotency_key`; the DB row commits first; if Temporal is unreachable the row stays `pending` and the worker's startup sweep picks it up.
- **Temporal** (`scheduler/src/workflows/publish.workflow.ts`): `scheduledPublish(id)` activity (5-min timeout) claims pending-only, resolves the connector via registry, publishes, records the publication, idempotent on re-run. - **Temporal** (`scheduler/src/workflows/publish.workflow.ts`): `scheduledPublish(id)` activity (5-min timeout) claims pending-only, resolves the connector via registry, publishes, records the publication, idempotent on re-run.
- **Receipts**: `GET /publishing/drafts/:draftId/publications` returns per-attempt outcomes (`ready/in_progress/completed/failed/unknown/abandoned`, `remoteId/remoteUrl`); the calendar page renders the latest outcome per scheduled draft as a badge with an open-remote link.
### 5.1 Per-connection publish governance (migration 033)
`connections.fields.publishGovernance` decides what happens when an approved draft targets that connection:
| Setting | Behavior on approval |
|---|---|
| unset | today's manual flow: a `publication.publish` holder calls `POST :draftId/publish` |
| `auto` | the approval itself schedules the publish (console approve or passing proposal poll) |
| `admin1` | a second chatgov poll (`subject_kind='draft_publish'`) opens; one admin yes releases |
| `admin_majority` | same, `quorum [{admins, x: floor(n/2)+1}]` snapshotted at open |
| `admin_consensus` | same, `[{admins, x: n}] join all`, veto allowed |
Route: `PATCH /connections/:id/publish-governance` (requires `chatgov.manage`). The publish route answers `202 {state:"awaiting_publish_approval", pollId, closesAt, rule}` while held; a passing poll schedules the publish as the original requester (re-checked at claim). The scheduler's **E3 pre-dispatch check** refuses any dispatch to a gated destination without a passed publish poll for the current approved revision — including schedules created before the gate was switched on. One open publish poll per (draft, destination), index-enforced, never colliding with review polls.
### 5.2 Governed boost (amplify)
`POST /publishing/drafts/:draftId/amplify {connectionId, kind: "repost"|"quote"}` executes the connector's `amplify` against `providerMetadata.sourceRemoteRef` recorded at draft creation — never a caller-supplied id. Target gates: draft state `approved` + the connection's publish governance. A gated boost holds with `202 awaiting_publish_approval` on the same publish-phase poll and is released by re-invoking amplify after the pass (hold-and-retry: no durable amplify queue exists, and the poll-close transaction must not do network I/O). Unset/`auto` connections behave exactly as before (`200` + execute).
## 6. Frontend (apps/web) ## 6. Frontend (apps/web)
@ -140,12 +159,14 @@ Live Source Monitor (adaptive polling, anti-flapping, per-host budgets); media e
| Concern | Path | | Concern | Path |
|---|---| |---|---|
| Tenant context / pools | `apps/api/src/db/client.ts` | | Tenant context / pools | `apps/api/src/db/client.ts` |
| Migrations 001–006 | `apps/api/migrations/` | | Migrations 001–034 | `apps/api/migrations/` |
| Vault / storage crypto | `apps/api/src/vault/vault.ts`, `apps/api/src/storage/storage.ts` | | Vault / storage crypto | `apps/api/src/vault/vault.ts`, `apps/api/src/storage/storage.ts` |
| Sessions / OIDC | `apps/api/src/auth/` | | Sessions / OIDC provider (issuer + refresh) | `apps/api/src/auth/`, `apps/api/src/oidc/` |
| RBAC guard | `apps/api/src/rbac/rbac.guard.ts` | | RBAC guard | `apps/api/src/rbac/rbac.guard.ts` |
| Audit chain | `migrations/001_tenants.sql` (SQL) + `apps/api/src/audit/` | | Audit chain | `migrations/001_tenants.sql` (SQL) + `apps/api/src/audit/` |
| Publishing | `apps/api/src/publishing/`, `apps/scheduler/src/` | | Publishing + publish governance + amplify | `apps/api/src/publishing/`, `apps/scheduler/src/activities/dispatch-gate.ts` |
| Matrix SSO broker | `apps/api/src/matrix-sso/` |
| Connector SDK | `packages/connector-sdk/src/` | | Connector SDK | `packages/connector-sdk/src/` |
| Frontend mock-registry | `apps/web/src/lib/` | | Frontend mock-registry | `apps/web/src/lib/` |
| Lint: no-provider-branching | `eslint.config.mjs` | | Lint: no-provider-branching | `eslint.config.mjs` |
| E2E: governance sim (S1–S9), brand palette guard | `scripts/e2e/governance-sim.mjs`, `scripts/e2e/brand-palette-check.mjs` |

View file

@ -152,6 +152,7 @@
- **Live.** Reads `GET /connections` for the workspace. A new workspace shows *"No channels connected yet"*, followed by one dashed *not connected* card per known platform. - **Live.** Reads `GET /connections` for the workspace. A new workspace shows *"No channels connected yet"*, followed by one dashed *not connected* card per known platform.
- **Connect buttons.** **Connect account**, **Connect**, **Reconnect** and **Remove** are **disabled when live**. Their tooltip reads: *"OAuth connect flows arrive with the connector wiring phase"*. **There is no way to add a channel from the UI today.** - **Connect buttons.** **Connect account**, **Connect**, **Reconnect** and **Remove** are **disabled when live**. Their tooltip reads: *"OAuth connect flows arrive with the connector wiring phase"*. **There is no way to add a channel from the UI today.**
- **Publish governance** (per connection): admins choose who signs off each publish — manual, automatic on approval, or an admin vote (1 / majority / consensus). Managed via the API (`PATCH /connections/:id/publish-governance`, admin guide §6.1); there is no web control for it yet. A governed connection holds publishes and boosts with *"awaiting publish approval"* until the admins' poll passes.
- **Each connected card** (seen in sample mode) shows: - **Each connected card** (seen in sample mode) shows:
- a health badge (*healthy*, *token expiring*, *needs credentials*) - a health badge (*healthy*, *token expiring*, *needs credentials*)
- capability chips (`publishText`, `amplifyRepost`, `feed`, `directMessages`, `comments`…) - capability chips (`publishText`, `amplifyRepost`, `feed`, `directMessages`, `comments`…)
@ -257,17 +258,11 @@ Invite emails and invite acceptance are **not wired**. The page says so itself.
## 11. Calendar, Approvals, Analytics, Settings ## 11. Calendar, Approvals, Analytics, Settings
**Status: stub.** Each of these four screens is a placeholder reading *"Arrives with WP3 (publishing pipeline)."* **Calendar** is the scheduled-drafts list (live against the API when signed in). Each row shows its draft, its schedule, and — since the receipts feature — a **dispatch receipt badge** for any draft that has been dispatched: the latest attempt's outcome (`completed` green, `failed` red, `in progress`/`ready` amber), with an **open ↗** link when the platform returned a post URL. Badges refresh on load, on schedule changes, every 30 s, and on **Refresh**. Drafts never dispatched show no badge.
| Calendar | Approvals | **Boosts (amplify).** A boost reposts or quotes someone else's post through one of your connections, and it is governed like any publish: the draft must be approved, and if the connection's publish governance is an admin mode (see the admin guide §6.1), the boost is held with *"awaiting publish approval"* until admins approve the publish poll — then re-send it and it executes. On ungoverned connections a boost executes immediately.
|---|---|
| ![](manual/screenshots/desktop-calendar.png) | ![](manual/screenshots/desktop-approvals.png) |
| Analytics | Settings | **Approvals / Analytics / Settings**: Approvals renders the review queue; Analytics and Settings are live screens (workspace settings, sessions, connections).
|---|---|
| ![](manual/screenshots/desktop-analytics.png) | ![](manual/screenshots/desktop-settings.png) |
The approval *state machine* does exist in the API: drafts move editing → in_review → approved, verified in §5. There is just no review-queue screen yet.
--- ---

View file

@ -27,3 +27,11 @@ Go to **More → Team** and use the invite link. Share the link with your crew
## 4. Connect your first accounts ## 4. Connect your first accounts
Go to **Connections** and add your platform accounts (Mastodon, Telegram, Bluesky, etc.). Each account's credentials are stored in an encrypted vault — the database never sees a plaintext key. Go to **Connections** and add your platform accounts (Mastodon, Telegram, Bluesky, etc.). Each account's credentials are stored in an encrypted vault — the database never sees a plaintext key.
## 5. Sign in to Files (your Drive)
Open **Files** from the Hub — no second password. Files (your cloud drive) uses your Hub sign-in: the first time, your account is created in the Drive automatically. If the Hub signs you out, the Drive follows within the hour. Same account, same crew, no extra credentials to leak.
## 6. Boosts and approvals
Boosting someone else's post works like publishing your own: the proposal is approved first, and if your collective enabled **publish approval** for that account, admins get a second vote before anything leaves. You'll see *"awaiting publish approval"* while it waits — resend once approved. Whether an account auto-publishes, needs one admin, a majority, or full consensus is your collective's choice (admins: see the admin guide).

View file

@ -27,3 +27,11 @@ Ve a **Más → Equipo** y usa el enlace de invitación. Comparte el enlace con
## 4. Conecta tus primeras cuentas ## 4. Conecta tus primeras cuentas
Ve a **Conexiones** y agrega tus cuentas (Mastodon, Telegram, Bluesky, etc.). Las credenciales de cada cuenta se guardan en una bóveda cifrada — la base de datos nunca ve una clave en texto plano. Ve a **Conexiones** y agrega tus cuentas (Mastodon, Telegram, Bluesky, etc.). Las credenciales de cada cuenta se guardan en una bóveda cifrada — la base de datos nunca ve una clave en texto plano.
## 5. Entra a Archivos (tu Drive)
Abre **Archivos** desde el Hub — sin segunda contraseña. Archivos (tu nube) usa tu sesión del Hub: la primera vez, tu cuenta se crea en el Drive automáticamente. Si cierras sesión en el Hub, el Drive te cierra también dentro de la hora. Misma cuenta, mismo equipo, sin credenciales extra que filtrar.
## 6. Boosts y aprobaciones
Impulsar (boost) una publicación ajena funciona como publicar la propia: la propuesta se aprueba primero y, si tu colectivo activó la **aprobación de publicación** para esa cuenta, los administradores votan otra vez antes de que salga nada. Verás *"esperando aprobación de publicación"* mientras espera — reenvíalo una vez aprobado. Si una cuenta publica sola, necesita un administrador, mayoría o consenso total, lo decide tu colectivo (administradores: ver la guía de administración).