#19514: feat: add Bluesky DM channel extension
size: XL
Cluster:
Messaging Channel Integrations
Add a new channel extension for Bluesky direct messages via the AT Protocol.
- App password authentication via @atproto/api
- DM polling with message deduplication
- Full ChannelPlugin implementation (config, pairing, security, outbound, status, gateway)
- 24 unit tests passing
- README with setup instructions and config reference
## Summary
- **Problem:** OpenClaw has no way to interact with Bluesky users — there is no Bluesky channel extension
- **Why it matters:** Bluesky is a growing decentralized social network with 20M+ users; agent access via DMs opens a new channel for users who prefer AT Protocol platforms
- **What changed:** Added `extensions/bluesky/` — a new channel plugin with 11 files implementing DM send/receive via `@atproto/api`, app password auth, polling-based message ingestion, and full ChannelPlugin adapter compliance
- **What did NOT change (scope boundary):** No changes to core OpenClaw code, existing extensions, or shared infrastructure. This is a pure additive extension.
## Change Type (select all)
- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [x] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [ ] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [x] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## Linked Issue/PR
None — this is a new feature contribution.
## User-visible / Behavior Changes
- New `bluesky` channel available via `openclaw plugins install @openclaw/bluesky`
- New config section `channels.bluesky` with `identifier`, `appPassword`, `service`, `pollIntervalMs`, `dmPolicy`, `allowFrom`
- Agent can receive and respond to Bluesky DMs when configured
## Security Impact (required)
- New permissions/capabilities? `Yes` — new channel that can send/receive Bluesky DMs on behalf of the configured account
- Secrets/tokens handling changed? `Yes` — app password stored in config (same pattern as all other channel extensions; env var substitution supported)
- New/changed network calls? `Yes` — XRPC calls to Bluesky PDS (`bsky.social` by default) for auth and chat API
- Command/tool execution surface changed? `No`
- Data access scope changed? `No`
- Risk + mitigation: App password scope is limited to what the user grants at creation time. The extension follows the same config/secret patterns as existing extensions (Nostr, Discord, etc.). Users are advised in the README to use env vars and dedicated app passwords.
## Repro + Verification
### Environment
- OS: macOS
- Runtime/container: Node.js (pnpm workspace)
- Model/provider: N/A (extension only)
- Integration/channel: Bluesky (AT Protocol)
- Relevant config: `channels.bluesky.identifier` + `channels.bluesky.appPassword`
### Steps
1. Run `npx vitest run --config vitest.extensions.config.ts extensions/bluesky/`
2. Observe 24 tests pass
### Expected
- All unit tests pass covering meta, capabilities, config, messaging, outbound, pairing, security, gateway, and status adapters
### Actual
- ✅ 24/24 tests pass
## Evidence
- [x] Failing test/log before + passing after
- [ ] Trace/log snippets
- [ ] Screenshot/recording
- [ ] Perf numbers (if relevant)
```
✓ extensions/bluesky/src/channel.test.ts (24 tests) 9ms
Test Files 1 passed (1)
Tests 24 passed (24)
```
## Human Verification (required)
- Verified scenarios: All unit tests pass; extension structure reviewed against Nostr extension for pattern compliance
- Edge cases checked: Unconfigured state (no identifier/appPassword), partial config (identifier only), DID vs handle normalization, @-prefix stripping, case sensitivity
- What you did **not** verify: Live end-to-end DM send/receive against a real Bluesky account (requires app password with DM scope)
## Compatibility / Migration
- Backward compatible? `Yes` — purely additive, no existing code modified
- Config/env changes? `Yes` — new optional `channels.bluesky` config section (no effect if not configured)
- Migration needed? `No`
## Failure Recovery (if this breaks)
- How to disable/revert this change quickly: Remove or disable the extension; set `channels.bluesky.enabled: false`; or uninstall via `openclaw plugins uninstall @openclaw/bluesky`
- Files/config to restore: Only `extensions/bluesky/` — self-contained, no other files touched
- Known bad symptoms reviewers should watch for: Auth failures if app password lacks DM scope ("Bad token scope" error)
## Risks and Mitigations
- Risk: Polling-based DM ingestion may miss messages if poll interval is too long or API rate limits are hit
- Mitigation: Default 5s interval is conservative; configurable via `pollIntervalMs`; errors are logged and don't crash the poll loop
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Added a new channel extension for Bluesky DM integration via AT Protocol. The implementation follows established patterns from other channel extensions (Nostr, Matrix) with proper plugin SDK integration, message polling, deduplication, and full ChannelPlugin adapter compliance.
**Major changes:**
- New `extensions/bluesky/` directory with complete channel plugin implementation
- Uses `@atproto/api` (v0.13.0) for authentication and DM operations
- Polling-based message ingestion with 5s default interval (configurable)
- DM policies: pairing, allowlist, open, disabled
- 24 unit tests covering meta, capabilities, config, messaging, outbound, pairing, security, gateway, and status adapters
**Issues found:**
- Missing handle-to-DID resolution in `sendDm` function — `getConvoForMembers` API requires DIDs, but the code accepts normalized handles without resolving them first
<h3>Confidence Score: 4/5</h3>
- This PR is mostly safe to merge with one logical issue that needs fixing
- The implementation follows established patterns and has comprehensive test coverage, but there's a critical bug where handles aren't resolved to DIDs before being passed to the AT Protocol API, which will cause runtime failures when users try to message via handles
- Pay close attention to `extensions/bluesky/src/bsky-chat.ts` — needs handle-to-DID resolution before the API call
<sub>Last reviewed commit: d3e4ce2</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#22260: feat(extensions/deltachat): add Delta.Chat channel extension
by alanz · 2026-02-20
81.9%
#20348: Add support for Keybase as a channel
by xgess · 2026-02-18
76.1%
#17157: feat(messenger): add Facebook Messenger channel integration
by gmjuhasz · 2026-02-15
75.3%
#9594: feat: add SimpleX messaging channel
by dangoldbj · 2026-02-05
74.7%
#10347: DingTalk: add channel support
by dimaginexus · 2026-02-06
74.1%
#9444: feat(channels): add DingTalk channel
by sm-yjr · 2026-02-05
73.4%
#19030: feat(simplex): add SimpleX Chat channel plugin
by Bladerunner-hue · 2026-02-17
73.0%
#21778: Nostr: enforce inbound DM policy and command authorization
by chansuke · 2026-02-20
72.9%
#21015: # feat(xmpp): Complete XMPP Channel Implementation
by toughworm · 2026-02-19
72.2%
#23464: feat(synology-chat): add group/channel support
by druide67 · 2026-02-22
72.2%