mobile bluesky app made with flutter
lazurite.stormlightlabs.org/
mobile
bluesky
flutter
1# Developer Tooling
2
3Lazurite includes two developer-focused features: an internal debug overlay for development
4and user-facing ATProto DevTools similar to [pdsls.dev](https://pdsls.dev/).
5
6## Overview
7
8| Feature | Availability | Purpose |
9| ---------------- | --------------- | ------------------------------------------------------ |
10| Debug Overlay | kDebugMode only | System diagnostics, network inspection, session info |
11| ATProto DevTools | All builds | Repository exploration, record inspection, JSON viewer |
12
13Both features share database tables but serve different audiences. The debug overlay helps
14developers diagnose issues during development. DevTools let power users explore their
15ATProto repository data.
16
17## Debug Overlay (Internal)
18
19### Activation
20
21Available only in Flutter debug builds (`kDebugMode == true`).
22
23- **Desktop:** ALT+F12
24- **Mobile:** 2-finger long-press for 2 seconds
25
26The overlay appears as a sliding drawer from the right edge.
27
28### Architecture
29
30**DebugOverlayHost** (`lib/src/features/debug/presentation/debug_overlay_host.dart`)
31wraps the entire app when in debug mode. It listens for activation gestures and renders
32the overlay as a Stack layer above the main content.
33
34**DebugOverlayController** (`lib/src/features/debug/application/debug_overlay_controller.dart`)
35manages visibility state and active tab selection via Riverpod StateNotifier.
36
37### Tabs
38
39**System Info** - Flutter version, platform, screen dimensions, memory usage
40
41**ATProto Session** - Current DID, handle, PDS host, session validity (tokens never shown)
42
43**Network Inspector** - Recent XRPC calls with method, status, duration. Captured by
44`DebugNetworkInterceptor` attached to Dio. Logs stored in `dev_network_logs` table with
45LRU eviction at 1000 entries.
46
47### Network Interception
48
49The interceptor (`lib/src/features/debug/infrastructure/debug_network_interceptor.dart`)
50captures all Dio requests/responses and persists them to the database.
51
52Security: Authorization headers must be redacted before storage. The interceptor should
53replace token values with `Bearer ***` to prevent accidental exposure.
54
55## ATProto DevTools (User-Facing)
56
57### Purpose
58
59Provide transparency into the user's ATProto repository. Users can browse collections,
60inspect records, and understand the underlying data structure. Read-only by default.
61
62### Access Control
63
64Gated by Settings toggle (stored in `dev_settings` table):
65
66- **Enable Developer Tools** - Shows DevTools in navigation
67- **Allow viewing other repos** - Enables browsing public DIDs beyond own repo
68- **Enable record editing** - Triple-gated write mode (deferred to parking lot)
69
70### Routes
71
72```text
73/devtools → DevToolsHomePage
74/devtools/collections → CollectionsPage
75/devtools/:collection → RecordsPage
76/devtools/:collection/:rkey → RecordDetailPage
77```
78
79All routes check the enabled toggle before rendering content.
80
81### Feature Structure
82
83```sh
84lib/src/features/developer_tools/
85├── application/
86│ ├── devtools_providers.dart # Riverpod providers
87│ └── dev_settings_providers.dart # Settings state
88├── domain/
89│ ├── repo_collection.dart # Collection model
90│ └── repo_record.dart # Record model with AT URI parsing
91├── infrastructure/
92│ └── devtools_repository.dart # ATProto API calls
93└── presentation/screens/
94 ├── dev_tools_home_page.dart # Entry point, DID display
95 ├── collections_page.dart # Collection browser with search
96 ├── records_page.dart # Paginated record list
97 └── record_detail_page.dart # JSON tree viewer
98```
99
100### Providers
101
102**devtoolsRepositoryProvider** - Repository wrapping XrpcClient for ATProto calls
103
104**collectionsProvider** - Fetches collections via `com.atproto.repo.describeRepo`
105
106**recordsProvider** - AsyncNotifierFamily for paginated records with cursor support
107
108**recordDetailProvider** - Fetches single record by collection + rkey
109
110**pinnedUrisProvider** - Streams pinned collections and records from database
111
112### Repository Methods
113
114`DevtoolsRepository` (`lib/src/features/developer_tools/infrastructure/devtools_repository.dart`)
115calls the user's PDS directly without the `atproto-proxy` header:
116
117- `describeRepo(did)` - List collections for a DID
118- `listRecords(repo, collection, limit, cursor)` - Paginated record list
119- `getRecord(repo, collection, rkey)` - Single record fetch
120
121Write methods (`createRecord`, `putRecord`, `deleteRecord`) exist in the spec but are
122not implemented pending write mode feature.
123
124### UI Components
125
126**CollectionsPage** - Searchable list with pin/unpin toggle. Tapping navigates to records.
127
128**RecordsPage** - Infinite scroll with cursor pagination. Rich previews for known types
129(posts, profiles, follows, likes). Pin button per record.
130
131**RecordDetailPage** - Metadata card (AT URI, CID, indexedAt) with copy buttons. JSON tree
132viewer using `flutter_json` package. Blob detection shows thumbnail previews for images.
133
134### Rich Previews
135
136Records are displayed with type-specific previews:
137
138- `app.bsky.feed.post` - Text content and createdAt timestamp
139- `app.bsky.actor.profile` - Display name and description
140- `app.bsky.graph.follow` - Target subject DID
141- `app.bsky.feed.like` - Liked subject URI
142- Unknown types - Fallback to generic JSON snippet
143
144## Database Schema
145
146Four tables in `lib/src/infrastructure/db/tables.dart`:
147
148**DevSettings** - Key-value store for feature toggles (enabled, allowOtherRepos,
149enableRecordEditing, maxCachedJsonBytes)
150
151**DevNetworkLogs** - Network request/response capture with uuid, method, url, statusCode,
152durationMs, headers, body, timestamp
153
154**DevPins** - Pinned collections and records with uri, label, type, createdAt
155
156**DevRecentRecords** - Recently viewed records with uri, cid, viewedAt (schema expansion
157planned for JSON caching)
158
159### DAO
160
161`DevToolsDao` (`lib/src/infrastructure/db/daos/dev_tools_dao.dart`) provides:
162
163- Settings: `getSetting()`, `setSetting()`
164- Network logs: `logRequest()`, `watchLogs()`, `clearLogs()`, `pruneLogs()`
165- Pins: `savePin()`, `deletePin()`, `watchPins()`
166- Recent records: `addRecentRecord()`, `watchRecentRecords()`, `pruneRecentRecords()`
167
168## Security Boundaries
169
170### Secrets Protection
171
172Never display or log:
173
174- Access tokens or refresh tokens
175- DPoP private keys
176- Secure storage keys
177
178Network logs must redact Authorization header values.
179
180### Other DID Inspection
181
182When viewing other users' repos:
183
184- Read-only operations only (describeRepo, listRecords, getRecord)
185- Display banner indicating public data view
186- Never call write endpoints for other DIDs
187
188### Defensive JSON Rendering
189
190Records may contain hostile or malformed JSON:
191
192- Limit tree depth to 50 levels
193- Truncate long strings in preview (1000 chars)
194- Handle deeply nested objects gracefully
195- Escape HTML entities in string values
196
197## Remaining Work (as of 2026-01-26)
198
199- Performance metrics tab in debug overlay
200- Settings toggle UI exposure
201- Other DID inspection feature
202- DevRecentRecords schema enhancement
203- Filter/sort controls in records list
204- File export functionality
205- Authorization header redaction
206
207## References
208
209- [pdsls.dev](https://pdsls.dev/) - ATProto explorer by @juli.ee
210- [com.atproto.repo.listRecords](https://docs.bsky.app/docs/api/com-atproto-repo-list-records)
211- [com.atproto.repo.getRecord](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)
212- [AT Protocol Repository Spec](https://atproto.com/specs/repository)