@abot-ai/socket 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +116 -2
- package/bin/abot-socket.mjs +15 -0
- package/config.example.json +14 -0
- package/docs/APP-REVIEW.md +184 -0
- package/docs/DEPLOYMENT.md +124 -0
- package/docs/INSTALLATION.md +166 -0
- package/docs/MIGRATION.md +48 -0
- package/docs/PROTOCOL.md +278 -0
- package/docs/SECURITY.md +98 -0
- package/docs/SELF-HOSTING.md +154 -0
- package/package.json +64 -4
- package/src/auth/auth-database.mjs +102 -0
- package/src/auth/auth-limits.mjs +17 -0
- package/src/auth/auth-pairing.mjs +95 -0
- package/src/auth/auth-sessions.mjs +119 -0
- package/src/auth/auth-storage-path.mjs +70 -0
- package/src/auth/auth.mjs +45 -0
- package/src/auth/runtime-credentials.mjs +39 -0
- package/src/cli/cli-arguments.mjs +23 -0
- package/src/cli/cli-entry.mjs +84 -0
- package/src/cli/cli-environment.mjs +31 -0
- package/src/cli/cli-help.mjs +34 -0
- package/src/cli/cli-output.mjs +35 -0
- package/src/cli/cli.mjs +58 -0
- package/src/cli/client-capabilities.mjs +4 -0
- package/src/cli/doctor.mjs +106 -0
- package/src/config/config-validation.mjs +41 -0
- package/src/config/config.mjs +57 -0
- package/src/deployment/deployment-command.mjs +42 -0
- package/src/deployment/deployment-input.mjs +76 -0
- package/src/deployment/deployment-plan.mjs +62 -0
- package/src/deployment/deployment-templates.mjs +109 -0
- package/src/main.mjs +26 -0
- package/src/notifications/notification-delivery-credentials.mjs +48 -0
- package/src/notifications/notification-delivery-fcm.mjs +100 -0
- package/src/notifications/notification-delivery-gateway.mjs +36 -0
- package/src/notifications/notification-delivery-http.mjs +53 -0
- package/src/notifications/notification-delivery-payload.mjs +45 -0
- package/src/notifications/notification-delivery.mjs +43 -0
- package/src/notifications/notification-gateway-command.mjs +48 -0
- package/src/notifications/notification-gateway-store.mjs +159 -0
- package/src/notifications/notification-gateway.mjs +181 -0
- package/src/notifications/notification-http.mjs +39 -0
- package/src/notifications/notification-input.mjs +82 -0
- package/src/notifications/notification-outbox.mjs +60 -0
- package/src/notifications/notification-preview-crypto.mjs +42 -0
- package/src/notifications/notification-preview-schema.mjs +7 -0
- package/src/notifications/notification-preview-store.mjs +63 -0
- package/src/notifications/notification-preview-text.mjs +20 -0
- package/src/notifications/notification-projection.mjs +20 -0
- package/src/notifications/notification-schema.mjs +22 -0
- package/src/notifications/notification-service.mjs +147 -0
- package/src/notifications/notification-store.mjs +132 -0
- package/src/pairing/pairing-qr-payload.mjs +32 -0
- package/src/pairing/pairing-qr.mjs +42 -0
- package/src/protocol/protocol-validation.mjs +137 -0
- package/src/protocol/protocol.mjs +110 -0
- package/src/relay/relay-client-admission.mjs +40 -0
- package/src/relay/relay-correlations.mjs +96 -0
- package/src/relay/relay-model-catalog.mjs +69 -0
- package/src/relay/relay-requests.mjs +155 -0
- package/src/relay/relay-runtime-registration.mjs +49 -0
- package/src/relay/relay-session-mutations.mjs +88 -0
- package/src/relay/relay.mjs +202 -0
- package/src/review/review-demo-isolation.mjs +132 -0
- package/src/review/review-demo-setup.mjs +97 -0
- package/src/review/review-portal-access.mjs +64 -0
- package/src/review/review-portal-form.mjs +24 -0
- package/src/review/review-portal-page.mjs +68 -0
- package/src/review/review-portal-server.mjs +105 -0
- package/src/setup/setup-files.mjs +37 -0
- package/src/setup/setup-questions.mjs +62 -0
- package/src/setup/setup-wizard.mjs +50 -0
- package/src/setup/setup.mjs +42 -0
- package/src/transport/http-api.mjs +31 -0
- package/src/transport/server.mjs +165 -0
- package/src/transport/transport-limits.mjs +85 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Moving to pairing
|
|
2
|
+
|
|
3
|
+
Installing the package does not change existing services or applications. Use a
|
|
4
|
+
separately approved cutover after real-device verification.
|
|
5
|
+
|
|
6
|
+
The chat envelopes, runtime bridge and runtime credential grants remain compatible.
|
|
7
|
+
Authentication changes: the new client uses local-operator pairing instead of
|
|
8
|
+
email. Old in-memory sessions cannot be imported. Existing devices must pair to
|
|
9
|
+
the new service, receiving separate credentials bound to its HTTPS origin.
|
|
10
|
+
|
|
11
|
+
1. Back up the current private configuration and deployed version.
|
|
12
|
+
2. Prepare a separate candidate endpoint and private state with a unique test
|
|
13
|
+
runtime identity. Do not attach two active runtimes using the same ID.
|
|
14
|
+
3. Issue pairing locally; verify pairing, restart persistence, runtime discovery,
|
|
15
|
+
history, chat, steering, approvals and revocation through the actual app.
|
|
16
|
+
4. Perform the approved service/client cutover while retaining a rollback path.
|
|
17
|
+
|
|
18
|
+
The new configuration has `publicOrigin`, `port`, `stateDirectory`, and
|
|
19
|
+
`runtimeGrants`. Remove old `allowedEmail` and `mail` settings; they are rejected.
|
|
20
|
+
`ABOT_SOCKET_CONFIG` accepts `SOCKET_V2_CONFIG` as a path alias. No mail-token
|
|
21
|
+
variable is used. The existing runtime adapter needs configuration only, not
|
|
22
|
+
source changes. Setup generates new runtime credentials rather than rotating or
|
|
23
|
+
migrating an existing installation automatically.
|
|
24
|
+
|
|
25
|
+
After cutover, retain the private state directory through upgrades. Device
|
|
26
|
+
sessions persist there; transient relay routes still disappear on restart.
|
|
27
|
+
Interrupted requests must be reconciled against runtime history before retry.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## Notification preview schema upgrade
|
|
31
|
+
|
|
32
|
+
Schema v3 adds a device-scoped preview-key table and nullable encrypted-preview
|
|
33
|
+
column on the existing outbox. Both schema v1 and v2 upgrade transactionally;
|
|
34
|
+
paired-device tokens, pending pairing, owner identity, notification metadata and
|
|
35
|
+
generic delivery queues are preserved. Preview remains off until that device
|
|
36
|
+
explicitly provisions a key after push registration. No runtime changes are needed.
|
|
37
|
+
|
|
38
|
+
Stop the service and back up the private state directory and matching deployed
|
|
39
|
+
code before upgrading. Older binaries reject schema v3. Rollback requires restoring
|
|
40
|
+
the matching pre-upgrade database snapshot together with old code while stopped;
|
|
41
|
+
do not point an old binary at the migrated state or delete the database to make it
|
|
42
|
+
start. Restoring a snapshot also restores its older device/revocation/delivery state,
|
|
43
|
+
so reconcile changes made since that snapshot before resuming service.
|
|
44
|
+
|
|
45
|
+
For gateway delivery, upgrade the publisher gateway before clients enable previews.
|
|
46
|
+
Keep previews off until compatible client support is installed. Old clients and
|
|
47
|
+
opted-out devices retain the original generic push behavior. This source change
|
|
48
|
+
alone does not deploy or enable preview delivery on an existing installation.
|
package/docs/PROTOCOL.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Protocol and compatibility
|
|
2
|
+
|
|
3
|
+
This candidate retains the Socket v2 chat transport contract and replaces email
|
|
4
|
+
authentication with operator-issued pairing. It does not change
|
|
5
|
+
runtime source or expose the complete runtime Web UI API. The `/health` value
|
|
6
|
+
`version: 2` identifies the protocol family, not a package release number.
|
|
7
|
+
|
|
8
|
+
## HTTP
|
|
9
|
+
|
|
10
|
+
Use the configured HTTPS origin. JSON body limits depend on the route:
|
|
11
|
+
|
|
12
|
+
| Route | Maximum JSON request body |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `POST /v2/pairing/redeem`, `POST /v2/notifications/read`, `POST /v2/notifications/presence` | 4 KiB (4096 bytes). |
|
|
15
|
+
| `PUT /v2/notifications/push` | 8 KiB (8192 bytes). |
|
|
16
|
+
| `PUT /v2/notifications/push/preview` | 512 bytes. |
|
|
17
|
+
|
|
18
|
+
These are application limits; a reverse proxy can impose a smaller limit. The
|
|
19
|
+
generated direct-proxy plans currently cap request bodies at 4 KiB. The separate
|
|
20
|
+
notification gateway accepts JSON bodies up to 8 KiB and needs its own proxy
|
|
21
|
+
configuration. Field validation still applies within each body limit.
|
|
22
|
+
|
|
23
|
+
Responses are `no-store`. Host must match the configured origin, and a supplied
|
|
24
|
+
browser Origin must match it exactly. The local proxy requirements apply to all
|
|
25
|
+
routes, including `/health`.
|
|
26
|
+
|
|
27
|
+
| Method and path | Request / response |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `GET /health` | `{ok:true, version:2}`; no account or runtime details. |
|
|
30
|
+
| `POST /v2/pairing/redeem` | `{pairingId, code, deviceName}` → `{token, expiresAt, session:{id,subject,deviceName}}`. |
|
|
31
|
+
| `GET /v2/auth/sessions` | Bearer session token → `{sessions:[{id,deviceName,createdAt,expiresAt,current}]}`. |
|
|
32
|
+
| `DELETE /v2/auth/sessions/:id` | Bearer session token → `{revoked}`. |
|
|
33
|
+
| `POST /v2/auth/logout` | Bearer session token → `{ok:true}`. |
|
|
34
|
+
| `GET /v2/runtimes` | Bearer session token → `{runtimes:[...]}` with IDs, online state, model catalog, allowed modes, and capabilities. |
|
|
35
|
+
|
|
36
|
+
`pairingId` is a random 32-character base64url temporary key; `code` is exactly
|
|
37
|
+
six decimal digits, including leading zeros. Both are required. A bundle is
|
|
38
|
+
valid for five minutes and five failed attempts; consume and device issuance
|
|
39
|
+
share a database transaction. A retry after a lost success response cannot reuse
|
|
40
|
+
it: the operator must create a fresh bundle. Neither secret belongs in a URL.
|
|
41
|
+
The ordinary Socket API has no HTTP endpoint for generating pairing bundles.
|
|
42
|
+
The optional, separately enabled [app-review portal](APP-REVIEW.md) exposes an
|
|
43
|
+
authenticated `POST /review/pair` on its own listener/origin, bound to a dedicated
|
|
44
|
+
review installation. It is not part of the ordinary Socket API. Legacy email
|
|
45
|
+
endpoints are no longer supported.
|
|
46
|
+
|
|
47
|
+
### Pairing QR, version 1
|
|
48
|
+
|
|
49
|
+
The operator CLI can encode the existing bundle as raw UTF-8 JSON in a QR:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{"type":"abot-pairing","version":1,"serverUrl":"https://socket.example.test","pairingId":"0123456789abcdefghijklmnopqrstuv","code":"000123","expiresAt":1800000300000}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This example is illustrative, not a usable pairing. `expiresAt` is Unix time in
|
|
56
|
+
milliseconds; the server remains authoritative for validity. The code is a string
|
|
57
|
+
to preserve leading zeros. The QR contains only these six fields, never a runtime
|
|
58
|
+
token, permanent device token, configuration file, or secret-bearing URL/deep link.
|
|
59
|
+
|
|
60
|
+
Clients validate the type/version and field shapes, accept only a canonical HTTPS
|
|
61
|
+
origin, display the destination for confirmation, and then use the existing
|
|
62
|
+
`POST /v2/pairing/redeem`. Scanning alone must not contact the destination. Suppress
|
|
63
|
+
duplicate camera detections while confirmation or redemption is in progress.
|
|
64
|
+
The existing expiry, attempt limits and one-use transaction apply unchanged;
|
|
65
|
+
creating a new bundle replaces only the pending pairing, not existing devices.
|
|
66
|
+
The QR is a bearer enrollment secret, so it must not be retained in logs or sent
|
|
67
|
+
to external QR-generation services.
|
|
68
|
+
|
|
69
|
+
An expired, unknown, consumed or wrong pairing returns `invalid_code` / 401.
|
|
70
|
+
General HTTP limits and source/global code-verification limits return `rate_limited` / 429.
|
|
71
|
+
Verification quotas apply only after matching an unexpired, unlocked pending pairing
|
|
72
|
+
key; malformed, unknown, expired or already-locked pairing requests do not spend them.
|
|
73
|
+
A full device list
|
|
74
|
+
returns `device_limit` / 429; free a slot locally. Malformed device names return
|
|
75
|
+
`invalid_request` / 400 without consuming an otherwise valid pairing.
|
|
76
|
+
|
|
77
|
+
## Native client WebSocket
|
|
78
|
+
|
|
79
|
+
Connect to `/v2/realtime` using `Authorization: Bearer <device-session-token>` in
|
|
80
|
+
the HTTP upgrade. Credentials are not accepted in URL query parameters. Native
|
|
81
|
+
clients should store the token in the platform's secure credential store.
|
|
82
|
+
Browser WebSocket APIs cannot set this header; browser support needs its own
|
|
83
|
+
designed authentication path.
|
|
84
|
+
|
|
85
|
+
On connection, the relay sends `authenticated` with the session expiry and
|
|
86
|
+
capability flags. Client commands use `{id, runtimeId, body}`. IDs must be unique
|
|
87
|
+
per connection. Supported body types are:
|
|
88
|
+
|
|
89
|
+
- `run_request`, `steer_request`, `tool_approval_response`;
|
|
90
|
+
- `session.list.request`, `session.snapshot.request`, `request.events.request`;
|
|
91
|
+
- `session.delete.request`, `session.messages.clear.request`, `session.message.delete.request`;
|
|
92
|
+
- `attachment.upload.request`, `attachment.fetch.request`;
|
|
93
|
+
- relay-local `subscribe_request` for a known request belonging to this owner.
|
|
94
|
+
|
|
95
|
+
The relay returns `relay.ack`, `relay.error`, `relay.response`, or `relay.event`
|
|
96
|
+
with the appropriate command ID, runtime ID, and body. An acknowledgement means
|
|
97
|
+
dispatched or queued, not completed. Runtime correlations belong to the relay;
|
|
98
|
+
the client cannot choose them. Unknown command fields and unsupported operations
|
|
99
|
+
are rejected rather than forwarded as a generic admin API.
|
|
100
|
+
|
|
101
|
+
Tool permission values are `ask`, `full_access`, and `full_plus`, constrained by
|
|
102
|
+
the runtime grant. Setup grants only `ask`. The model catalog and supported
|
|
103
|
+
reasoning controls come from the connected runtime, not a hardcoded model list.
|
|
104
|
+
|
|
105
|
+
### Session mutations
|
|
106
|
+
|
|
107
|
+
Each runtime in `/v2/runtimes` and the WebSocket `authenticated` frame includes
|
|
108
|
+
`capabilities.sessionMutation: true` for these three relay operations. Clients must treat an
|
|
109
|
+
absent/false flag as unsupported and require explicit user confirmation.
|
|
110
|
+
|
|
111
|
+
| Request body | Response body (inside `relay.response`) |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `{type:'session.delete.request', sessionId}` | `{type:'session.delete.response', ok, sessionId, deleted, deletedMessages, deletedRequests}` |
|
|
114
|
+
| `{type:'session.messages.clear.request', sessionId}` | `{type:'session.messages.clear.response', ok, sessionId, cleared, deletedMessages, deletedRequests, updatedAt}` |
|
|
115
|
+
| `{type:'session.message.delete.request', sessionId, messageId}` | `{type:'session.message.delete.response', ok, sessionId, messageId, deleted, updatedAt, deletedRequestId?, deletedRequestEvents?}` |
|
|
116
|
+
|
|
117
|
+
Use canonical message IDs from a fresh runtime snapshot, not local UI IDs.
|
|
118
|
+
Numeric message IDs are normalized to strings. Only `ok:true` together with
|
|
119
|
+
`deleted:true` / `cleared:true` confirms a change; reload canonical history after
|
|
120
|
+
the result. The runtime's single-message delete removes only that message row,
|
|
121
|
+
plus its associated request replay/context and memory checkpoints; sibling
|
|
122
|
+
message rows remain. Session deletion/clearing follows the existing runtime's
|
|
123
|
+
history and attachment cleanup semantics.
|
|
124
|
+
|
|
125
|
+
The relay rejects observed active/uncertain requests and in-flight uploads with
|
|
126
|
+
`session_busy`. Before forwarding a mutation it reads a complete snapshot with
|
|
127
|
+
`includeRequests:true`; active requests prevent deletion, and an incomplete or
|
|
128
|
+
unrecognized snapshot returns `session_state_unavailable`. No snapshot contents
|
|
129
|
+
are added to the mutation response. A pending mutation blocks another mutation,
|
|
130
|
+
new run or upload in that session (`session_mutation_pending`). It retains this
|
|
131
|
+
guard after client disconnect until the outcome or the shared 30-second timeout.
|
|
132
|
+
A client disconnected during the read check cannot begin deletion.
|
|
133
|
+
|
|
134
|
+
This check is not an atomic lock over independent Web UI clients. A request
|
|
135
|
+
started directly at the runtime after the check can race with deletion; the
|
|
136
|
+
runtime fences deleted generations and that request may be cancelled. Wait for
|
|
137
|
+
work on all clients to finish. `outcome_unknown` never authorizes an automatic
|
|
138
|
+
retry: reload authoritative history first. Missing sessions report
|
|
139
|
+
`session_not_found`; runtime failures remain failures.
|
|
140
|
+
|
|
141
|
+
## Runtime bridge
|
|
142
|
+
|
|
143
|
+
Connect to `/agent-bridge` with the separate runtime Bearer token, then send the
|
|
144
|
+
existing `{type:'register', agentId, features?, modelCatalog?}` message. The
|
|
145
|
+
registered ID must match the credential's grant. A second connection cannot
|
|
146
|
+
replace an already connected runtime with the same ID. Client device credentials
|
|
147
|
+
cannot authenticate this role.
|
|
148
|
+
|
|
149
|
+
## Recovery and capability limits
|
|
150
|
+
|
|
151
|
+
- A timeout or disconnect can return `outcome_unknown`; do not retry commands
|
|
152
|
+
automatically. Read canonical runtime history/replay to reconcile the result.
|
|
153
|
+
- Live `eventSequence` and replay `seqNo` are separate counters. `afterSeq` uses
|
|
154
|
+
the persisted replay counter only.
|
|
155
|
+
- Approval authority requires an observed pending approval for the correct
|
|
156
|
+
runtime and request. Historical replay alone does not restore that authority.
|
|
157
|
+
Positive approval expires after five minutes; an explicit decline can remain
|
|
158
|
+
possible once on that same active runtime connection and request.
|
|
159
|
+
- `cancel` and `durableApprovals` are advertised as false. Restart, cron,
|
|
160
|
+
configuration, and generic administration are not exposed.
|
|
161
|
+
- The relay keeps bounded temporary routing state. It is not an offline queue,
|
|
162
|
+
durable conversation store, or exactly-once execution service.
|
|
163
|
+
|
|
164
|
+
Use the new pairing-capable client. Email-only personal builds cannot enroll
|
|
165
|
+
through this authentication contract. Existing runtime adapters are unchanged.
|
|
166
|
+
|
|
167
|
+
## Notifications
|
|
168
|
+
|
|
169
|
+
`capabilities.notifications: true` advertises the authenticated notification API.
|
|
170
|
+
Notifications are owned by the **paired device that initiated the request**, not
|
|
171
|
+
by its current WebSocket connection. Other paired devices cannot list or mark
|
|
172
|
+
that device's records. There are no new unsolicited WebSocket frame types;
|
|
173
|
+
clients poll the inbox while foregrounded and reconcile on resume or push tap.
|
|
174
|
+
|
|
175
|
+
| Method and path | Request / response |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| `GET /v2/notifications?limit=50&before=<cursor>` | Device inbox, descending time; limit 1–100; opaque optional cursor. |
|
|
178
|
+
| `POST /v2/notifications/read` | `{ids:[...]}` (maximum 100) → `{ok:true}`. Unknown or foreign IDs have no effect. |
|
|
179
|
+
| `POST /v2/notifications/presence` | `{foreground:boolean,runtimeId?,sessionId?}` → `{ok:true}`; a session requires its runtime. |
|
|
180
|
+
| `PUT /v2/notifications/push` | `{provider:'fcm',token}` or `{provider:'gateway',endpointId,sendToken}` → `{ok:true}`. |
|
|
181
|
+
| `DELETE /v2/notifications/push` | Remove this device's push registration, preview key and queued deliveries → `{ok:true}`. |
|
|
182
|
+
| `PUT /v2/notifications/push/preview` | Exact `{keyId,key}` → `{ok:true}`; opt in after registering push. Replaces the device's key. |
|
|
183
|
+
| `DELETE /v2/notifications/push/preview` | Remove this device's preview key and queued ciphertext, retaining generic deliveries → `{ok:true}`. |
|
|
184
|
+
|
|
185
|
+
The inbox response is `{version:1,items,nextCursor,unreadCount,unreadByRuntime,
|
|
186
|
+
unreadBySession,delivery,registration,preview}`. Each item is
|
|
187
|
+
`{id,runtimeId,sessionId,requestId,kind,createdAt,readAt,expiresAt}`; timestamps
|
|
188
|
+
are epoch milliseconds, and `readAt` is null until read. `kind` is `completed`,
|
|
189
|
+
`failed` or `approval_required`. `unreadByRuntime` maps runtime IDs to counts;
|
|
190
|
+
`unreadBySession` maps runtime IDs to session-ID/count maps. `delivery` is
|
|
191
|
+
`{mode:'disabled'|'fcm'|'gateway',configured:boolean}`, and `registration` is
|
|
192
|
+
`{enabled:boolean}`. `preview` is `{supported:true,enabled:boolean,keyId:string|null}`.
|
|
193
|
+
Older servers omit `preview`; clients treat that as unsupported and off. Keys
|
|
194
|
+
and registration credentials are never returned by this API.
|
|
195
|
+
|
|
196
|
+
Foreground presence lasts 45 seconds. Renew while active; `foreground:false`
|
|
197
|
+
clears it. Presence is advisory: the phone may be suspended before its background
|
|
198
|
+
update arrives, so presence alone never marks records read or suppresses delivery.
|
|
199
|
+
Only an explicit authenticated read acknowledgement consumes records and cancels
|
|
200
|
+
queued deliveries. Clients acknowledge fetched IDs after showing the conversation.
|
|
201
|
+
Counts share the same ledger; they are not runtime transcript read receipts.
|
|
202
|
+
Push taps resolve the notification ID through this authenticated inbox
|
|
203
|
+
before opening a conversation; a push payload grants no execution or approval authority.
|
|
204
|
+
|
|
205
|
+
Only validated live `completed`, `failed` and `tool.approval.required` frames for
|
|
206
|
+
known relay requests create records. Streaming/progress events and replay do not.
|
|
207
|
+
Granted/rejected/terminal events cancel pending approval deliveries. Approval
|
|
208
|
+
notifications expire after five minutes; other records after at most seven days
|
|
209
|
+
or device credential expiry. Storage is capped at 500 records per device.
|
|
210
|
+
Expiration, logout and revocation remove registrations and pending deliveries.
|
|
211
|
+
The outbox retries at most five times, with bounded backoff. Duplicate source
|
|
212
|
+
events are deduplicated, but provider delivery is not an exactly-once guarantee.
|
|
213
|
+
|
|
214
|
+
The inbox and queued deliveries survive relay restarts. In-flight request routing
|
|
215
|
+
does not: this is not a complete runtime-wide event subscription or an offline
|
|
216
|
+
execution queue. Web UI, schedules and autonomous SPARK events are outside this
|
|
217
|
+
phase. Never interpret a missing notification as a failed request or auto-retry it.
|
|
218
|
+
|
|
219
|
+
Push is disabled unless private operator configuration includes
|
|
220
|
+
`notifications.delivery`. A self-built client uses
|
|
221
|
+
`{type:'fcm',serviceAccountFile:'./private-sender.json'}`; the path is resolved
|
|
222
|
+
relative to the private socket configuration, and sender credentials stay on
|
|
223
|
+
that operator's server. The client must belong to the same configured Firebase
|
|
224
|
+
project. Official builds can use `{type:'gateway',gatewayOrigin:'https://<trusted-host>'}`
|
|
225
|
+
with a separately enrolled endpoint. Clients cannot choose a gateway URL or send
|
|
226
|
+
provider credentials through the registration API. A missing or mismatched
|
|
227
|
+
configured transport returns `409 push_unavailable`; ordinary chat and the inbox
|
|
228
|
+
continue to work with delivery disabled.
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
### Optional encrypted answer previews
|
|
232
|
+
|
|
233
|
+
Previews are off by default and enabled per paired device, only after an active
|
|
234
|
+
push registration exists. The app generates a random 32-byte AES-256-GCM key and
|
|
235
|
+
16-byte key ID, sends both over authenticated HTTPS to its own Socket, and enables
|
|
236
|
+
local display only after acknowledgement. Both fields use canonical unpadded
|
|
237
|
+
base64url. No email, additional identity provider, or runtime change is involved.
|
|
238
|
+
Provider-token rotation preserves the preview setting. Key replacement clears
|
|
239
|
+
pending ciphertext. Disabling previews clears the key and ciphertext while leaving
|
|
240
|
+
the generic queue intact; unregistering push removes the entire delivery queue.
|
|
241
|
+
In-flight sends are aborted where possible, but provider-accepted notifications
|
|
242
|
+
cannot be recalled. The client must discard its key immediately on disable/logout.
|
|
243
|
+
|
|
244
|
+
Only a nonempty final `completed.output` is eligible. The Socket takes a bounded
|
|
245
|
+
prefix, removes unsafe controls and bidi override characters, and truncates on a
|
|
246
|
+
Unicode scalar boundary to at most 1024 UTF-8 bytes, including an ellipsis when
|
|
247
|
+
needed. It is an excerpt, not secret redaction. Approval, failure and progress
|
|
248
|
+
content is never included. No plaintext preview is stored in the inbox or outbox.
|
|
249
|
+
The inbox remains metadata-only; encrypted content exists only in the pending
|
|
250
|
+
outbox and is removed on delivery, read acknowledgement, revocation or expiry.
|
|
251
|
+
|
|
252
|
+
The exact encrypted envelope is `{v:1,keyId,nonce,ciphertext,expiresAt}`. Nonce is
|
|
253
|
+
12 random bytes. Ciphertext contains encrypted UTF-8 followed by the 16-byte GCM
|
|
254
|
+
authentication tag (at most 1040 bytes), using canonical unpadded base64url.
|
|
255
|
+
Authenticated additional data is UTF-8, with literal newline separators:
|
|
256
|
+
|
|
257
|
+
```text
|
|
258
|
+
ABot push preview v1
|
|
259
|
+
<keyId>
|
|
260
|
+
<notificationId>
|
|
261
|
+
<expiresAt decimal epoch milliseconds>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The expiry equals notification expiry, capped by the device credential and seven
|
|
265
|
+
days. The device rejects an unknown key, altered ID/expiry/content, expired data
|
|
266
|
+
or malformed envelope and retains a generic alert. The gateway receives neither
|
|
267
|
+
the key nor plaintext. Its `/v1/deliver` accepts an optional strictly validated
|
|
268
|
+
`preview` field alongside the existing fixed delivery fields. Upgrade the gateway
|
|
269
|
+
before enabling previews; older gateways do not accept this field.
|
|
270
|
+
|
|
271
|
+
Encrypted FCM sends have no top-level `notification` or `android.notification`.
|
|
272
|
+
Their string-valued data is `notificationId`, `abotPreview` (envelope JSON),
|
|
273
|
+
`abotKind` (`completed`), `abotExpiresAt` and `abotBadge`. Android renders the
|
|
274
|
+
notification locally. APNs retains a generic alert and sets `mutable-content:1`
|
|
275
|
+
for the app's notification-service extension. Generic sends keep their existing
|
|
276
|
+
payload. Serialized provider envelopes are bounded to 4096 UTF-8 bytes. Foreground
|
|
277
|
+
display and navigation continue to use the authenticated inbox; push cannot grant
|
|
278
|
+
access or approval authority.
|
package/docs/SECURITY.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Security boundaries
|
|
2
|
+
|
|
3
|
+
ABot Socket is a single-owner relay. Each installation has its own runtime
|
|
4
|
+
grants, device credentials, private state and HTTPS origin. Every paired device
|
|
5
|
+
represents that owner; this is not tenant isolation for unrelated users.
|
|
6
|
+
|
|
7
|
+
## Authentication and credentials
|
|
8
|
+
|
|
9
|
+
- Ordinary installations issue pairing through local operator commands. A random temporary
|
|
10
|
+
pairing key and six-digit code expire after five minutes or one successful use.
|
|
11
|
+
Five incorrect code attempts invalidate the challenge; expiry and attempts
|
|
12
|
+
persist across server restarts. Source and global verification limits apply only
|
|
13
|
+
to requests identifying an unexpired, unlocked pending pairing key. Unrelated
|
|
14
|
+
requests do not spend that quota; general HTTP request limits still apply.
|
|
15
|
+
- Generating fresh pairing details replaces only the pending challenge, not
|
|
16
|
+
existing devices. Consumption and device creation are atomic in SQLite.
|
|
17
|
+
- Device tokens are independently random. Only token digests are stored by the
|
|
18
|
+
server. Sessions last at most 30 days without sliding renewal, survive restarts
|
|
19
|
+
and can be revoked locally or by an authenticated owner device. Revocation and
|
|
20
|
+
expiry close existing WebSockets. At most 10 active device sessions are allowed.
|
|
21
|
+
- The app binds credentials to the selected HTTPS origin in iOS Keychain and
|
|
22
|
+
refuses redirects. The pairing key is temporary, not a permanent shared key.
|
|
23
|
+
- Runtime tokens are separate and bound to one runtime ID. Only their SHA-256
|
|
24
|
+
digest is in server configuration. Never put a runtime token into the app.
|
|
25
|
+
- Configuration is private; state directories are 0700 and files 0600. State
|
|
26
|
+
symlinks, unexpected entries and unsafe ownership/permissions are rejected.
|
|
27
|
+
Keep installation files outside source, and never log codes, keys or bodies.
|
|
28
|
+
- Setup and `pair` intentionally display new one-time details to the operator.
|
|
29
|
+
The ordinary Socket HTTP API does not generate or expose pairing bundles and
|
|
30
|
+
offers no email fallback or login bypass.
|
|
31
|
+
- A separately enabled [app-review portal](APP-REVIEW.md) can generate and display
|
|
32
|
+
bundles after reviewer authentication. It binds to a dedicated review
|
|
33
|
+
installation, uses a separate listener/origin and must not share production
|
|
34
|
+
state or runtime credentials. Protect its access details as credentials.
|
|
35
|
+
|
|
36
|
+
## Trusted components
|
|
37
|
+
|
|
38
|
+
The relay, its local reverse proxy and runtime computers are trusted. There is no
|
|
39
|
+
email delivery or third-party identity provider. HTTPS/WSS protects transport;
|
|
40
|
+
the relay can read routed messages and attachments. This is not end-to-end
|
|
41
|
+
encryption against its operator or a compromised relay host.
|
|
42
|
+
|
|
43
|
+
Follow [the proxy requirements](SELF-HOSTING.md#3-configure-https-and-the-proxy-boundary).
|
|
44
|
+
The listener is private loopback. Proxy headers must be overwritten by a trusted
|
|
45
|
+
local proxy; forwarded addresses are not trustworthy simply because a header
|
|
46
|
+
exists. CDN chains require a separate trusted-peer policy.
|
|
47
|
+
|
|
48
|
+
## Permission and routing limits
|
|
49
|
+
|
|
50
|
+
Runtime grants limit the permitted tool modes; `ask` is the setup default.
|
|
51
|
+
Requests and responses are bound to their runtime connection and private
|
|
52
|
+
correlation. Clients cannot use this relay to call generic runtime administration,
|
|
53
|
+
restart, or scheduling. Session deletion/clearing is limited to three validated
|
|
54
|
+
commands for an authorized runtime, with private response correlations and a
|
|
55
|
+
read-before-delete check for active work. See the
|
|
56
|
+
[mutation contract and its cross-client race limit](PROTOCOL.md#session-mutations).
|
|
57
|
+
|
|
58
|
+
The relay validates command shapes, bounds frames, connections and routing state,
|
|
59
|
+
and rejects unsupported operations. These controls do not make a compromised
|
|
60
|
+
authorized device harmless: it can act within its owner's configured grants.
|
|
61
|
+
Revoking that device session removes its relay access.
|
|
62
|
+
|
|
63
|
+
The relay does not automatically retry uncertain operations, restore approval
|
|
64
|
+
authority from historical events, or provide exactly-once execution. After a
|
|
65
|
+
disconnect, reconcile against runtime history before resubmission.
|
|
66
|
+
|
|
67
|
+
## Push preview boundary
|
|
68
|
+
|
|
69
|
+
Answer previews are opt-in per paired device and off by default. The already
|
|
70
|
+
trusted Socket sees final answers and encrypts a bounded excerpt with a random
|
|
71
|
+
per-device AES-256-GCM key provisioned over the existing authenticated HTTPS API.
|
|
72
|
+
That shared key is stored in the restricted Socket state; it is not sent to the
|
|
73
|
+
publisher gateway, FCM or APNs. The gateway validates and forwards ciphertext only.
|
|
74
|
+
This protects preview content from delivery intermediaries; it does not protect
|
|
75
|
+
against a compromised Socket, device, or its operator. It is not a transcript
|
|
76
|
+
end-to-end encryption claim.
|
|
77
|
+
|
|
78
|
+
Keys are scoped to the paired device, and notification ID and expiry are bound
|
|
79
|
+
into authenticated data. Key replacement, disable, logout and revocation remove
|
|
80
|
+
queued ciphertext; provider token rotation retains the key. The inbox never
|
|
81
|
+
returns preview content or keys. Pending ciphertext is deleted after delivery,
|
|
82
|
+
read or expiry. SQLite deletion is logical cleanup, not forensic secure erasure;
|
|
83
|
+
protect state backups as credentials. The OS may retain displayed notifications,
|
|
84
|
+
and previews can reveal answer content on a lock screen according to user settings.
|
|
85
|
+
Disabling cannot recall alerts already accepted or displayed by the provider/OS.
|
|
86
|
+
The device must remove its local key immediately so later queued ciphertext falls
|
|
87
|
+
back to generic text. Unsupported, expired or invalid payloads also fail generic.
|
|
88
|
+
|
|
89
|
+
## Validation status
|
|
90
|
+
|
|
91
|
+
Local tests use dummy principals, a mock runtime. Some
|
|
92
|
+
tests exercise loopback HTTP/WebSocket transport. Configuration checking itself
|
|
93
|
+
does not send email or open a listener. None of these checks proves a deployed
|
|
94
|
+
proxy, domain, or real client/runtime behavior.
|
|
95
|
+
|
|
96
|
+
Package publication is not a claim of a completed independent security
|
|
97
|
+
audit. When investigating a problem, use synthetic credentials and redact
|
|
98
|
+
configuration, tokens, codes, and conversation content from reports.
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Self-hosting ABot Socket
|
|
2
|
+
|
|
3
|
+
Use [Installation and first connection](INSTALLATION.md) for the consumer
|
|
4
|
+
installation, setup and QR/manual pairing flow. This companion guide covers
|
|
5
|
+
configuration, the proxy trust boundary and ongoing operation. Service/proxy plan
|
|
6
|
+
generation is covered in [Deployment](DEPLOYMENT.md). The source is MIT-licensed;
|
|
7
|
+
the package is distributed as `@abot-ai/socket` on npm.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Node.js 24 or later. npm installs the package's pinned dependencies, `ws` and
|
|
12
|
+
`qrcode-generator`, with either the registry package or a local tarball.
|
|
13
|
+
- A server you control and a canonical HTTPS origin such as
|
|
14
|
+
`https://socket.example.net`.
|
|
15
|
+
- A local HTTPS reverse proxy satisfying the boundary below.
|
|
16
|
+
- An ABot Runtime with its existing agent-bridge adapter, and ABot App with pairing.
|
|
17
|
+
|
|
18
|
+
There is no public signup. Every paired device acts as this installation's single
|
|
19
|
+
owner and can see all runtimes granted to that installation.
|
|
20
|
+
|
|
21
|
+
## 1. Prepare a private installation
|
|
22
|
+
|
|
23
|
+
Follow the [setup steps](INSTALLATION.md#follow-setup) as the unprivileged account
|
|
24
|
+
that will run the relay. Use your own HTTPS origin and keep the configuration,
|
|
25
|
+
runtime connection file and state outside the installed package directory.
|
|
26
|
+
Setup creates files only; applying a deployment plan and configuring the runtime
|
|
27
|
+
remain separate operator actions.
|
|
28
|
+
|
|
29
|
+
## 2. Check configuration and issue fresh pairing details
|
|
30
|
+
|
|
31
|
+
For an installation outside the default `~/.abot-socket` directory:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
abot-socket check-config --config /srv/abot-private/config.json
|
|
35
|
+
abot-socket pair --config /srv/abot-private/config.json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`stateDirectory` is resolved relative to the configuration file, independent of
|
|
39
|
+
the process working directory. An explicit `--config` takes precedence over
|
|
40
|
+
environment settings. Otherwise use `ABOT_SOCKET_CONFIG`; `SOCKET_V2_CONFIG`
|
|
41
|
+
remains a legacy alias and conflicting environment values are rejected.
|
|
42
|
+
No mail credential is used.
|
|
43
|
+
|
|
44
|
+
`check-config` validates the file without opening listeners or state. `pair`
|
|
45
|
+
updates only local authentication state: it replaces any unused pairing details
|
|
46
|
+
and displays a new five-minute bundle. Already paired devices stay connected.
|
|
47
|
+
The running server sees these changes without a restart. Local operator commands
|
|
48
|
+
must run as the same operating-system user that owns the private state directory.
|
|
49
|
+
Generate pairing details after HTTPS is ready, then follow the
|
|
50
|
+
[QR/manual pairing flow](INSTALLATION.md#connect-runtime-then-pair-the-app).
|
|
51
|
+
|
|
52
|
+
## 3. Configure HTTPS and the proxy boundary
|
|
53
|
+
|
|
54
|
+
The application listens only on `127.0.0.1` at the configured port. Keep the proxy
|
|
55
|
+
on the same host and the application listener private. Containers, remote proxies,
|
|
56
|
+
and additional proxy chains require a separately validated network arrangement;
|
|
57
|
+
this package does not ship a Docker deployment.
|
|
58
|
+
|
|
59
|
+
For direct Internet-to-proxy-to-relay deployment, the proxy must:
|
|
60
|
+
|
|
61
|
+
1. Accept only the configured HTTPS authority and a valid certificate; preserve
|
|
62
|
+
the exact configured Host, including a non-default port if one is configured.
|
|
63
|
+
2. Support WebSocket upgrades on `/v2/realtime` and `/agent-bridge`, retaining the
|
|
64
|
+
`Authorization` header.
|
|
65
|
+
3. Remove incoming `X-Forwarded-Proto` and `X-Socket-Client-IP` values and set its
|
|
66
|
+
own: `https` for the verified HTTPS request and the actual client IP observed
|
|
67
|
+
on the proxy's connection. Never copy a client-supplied forwarding header.
|
|
68
|
+
4. Preserve an incoming browser `Origin` so the relay can reject an origin that
|
|
69
|
+
differs from its configured public origin.
|
|
70
|
+
5. Enforce connection, request-body, and timeout limits. Keep credentials, pairing
|
|
71
|
+
codes, request bodies, and authorization headers out of logs.
|
|
72
|
+
|
|
73
|
+
If a CDN or another proxy sits in front, deriving the client IP requires an
|
|
74
|
+
explicit trusted-peer policy. Trusting its claimed IP header from arbitrary
|
|
75
|
+
Internet peers is unsafe. The direct-proxy instructions above do not configure
|
|
76
|
+
that extra trust chain.
|
|
77
|
+
|
|
78
|
+
## 4. Start and connect
|
|
79
|
+
|
|
80
|
+
Use the [foreground or Linux service instructions](INSTALLATION.md#configure-https-and-start-the-relay),
|
|
81
|
+
then [connect the runtime and pair the app](INSTALLATION.md#connect-runtime-then-pair-the-app).
|
|
82
|
+
The app binds its device credential to the exact HTTPS origin and stores it in
|
|
83
|
+
the platform's protected credential storage. It does not follow authentication
|
|
84
|
+
or WebSocket redirects. A new server requires a new pairing; credentials are
|
|
85
|
+
never silently copied across hosts.
|
|
86
|
+
|
|
87
|
+
## Optional push delivery
|
|
88
|
+
|
|
89
|
+
Push is disabled by default. To enable it, the operator adds
|
|
90
|
+
`notifications.delivery` to the private Socket configuration and restarts the
|
|
91
|
+
relay deliberately:
|
|
92
|
+
|
|
93
|
+
- A self-built app uses its own Firebase project and
|
|
94
|
+
`{ "type": "fcm", "serviceAccountFile": "./private-sender.json" }`.
|
|
95
|
+
The app and sender must belong to the same Firebase project. Keep the service
|
|
96
|
+
account on the server, owned by the service user with mode `0600`; a relative
|
|
97
|
+
path is resolved from the Socket configuration file.
|
|
98
|
+
- A Store build uses its publisher's trusted gateway with
|
|
99
|
+
`{ "type": "gateway", "gatewayOrigin": "https://<trusted-host>" }`.
|
|
100
|
+
Obtain that origin from the publisher's instructions. The publisher operates
|
|
101
|
+
the gateway and Firebase credentials; each user operates their own Socket.
|
|
102
|
+
Do not enter provider credentials into the app or run a second gateway merely
|
|
103
|
+
to use the Store build.
|
|
104
|
+
|
|
105
|
+
After server configuration, enable notifications in the app and grant platform
|
|
106
|
+
permission. Gateway builds enroll their own endpoint before registering it with
|
|
107
|
+
the paired Socket. A mismatched delivery mode is rejected; the app cannot replace
|
|
108
|
+
the operator's gateway URL. See [the notification protocol](PROTOCOL.md#notifications)
|
|
109
|
+
for device ownership, retention, delivery limitations and optional encrypted
|
|
110
|
+
answer previews. Gateway operation is separately described in
|
|
111
|
+
[Deployment](DEPLOYMENT.md#optional-notification-gateway).
|
|
112
|
+
|
|
113
|
+
## Operation and recovery
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
abot-socket devices --config /srv/abot-private/config.json
|
|
117
|
+
abot-socket revoke <device-id> --config /srv/abot-private/config.json
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The device list contains names, identifiers and expiry, never tokens. Revocation
|
|
121
|
+
also closes an already-open client WebSocket within the next validity sweep
|
|
122
|
+
(approximately one second). At 10 active devices, revoke an unused device before
|
|
123
|
+
pairing another. The server never silently evicts an existing device.
|
|
124
|
+
|
|
125
|
+
Sessions have a fixed maximum lifetime of 30 days and survive server restarts.
|
|
126
|
+
After expiry, pair again. Reinstalling the app may require pairing again depending
|
|
127
|
+
on the platform's secure-storage lifecycle; retaining the device's credential is
|
|
128
|
+
not a guarantee across uninstall, signing or bundle-ID changes.
|
|
129
|
+
|
|
130
|
+
Back up the private configuration, runtime credential and state directory using
|
|
131
|
+
a consistent filesystem/database snapshot or with the service stopped. Losing
|
|
132
|
+
state requires pairing devices again. Restoring an old backup may restore old
|
|
133
|
+
revocation state; reapply necessary revocations. Conversation history remains in
|
|
134
|
+
each runtime. Temporary relay routes do not survive restart, so reconcile
|
|
135
|
+
interrupted requests with runtime history rather than automatically resending.
|
|
136
|
+
|
|
137
|
+
The package does not provision a public TLS endpoint or install a service.
|
|
138
|
+
Real-device, proxy and runtime verification remains separate from automated tests.
|
|
139
|
+
|
|
140
|
+
## Source-checkout development
|
|
141
|
+
|
|
142
|
+
These contributor commands require the repository checkout containing
|
|
143
|
+
`package.json`, `package-lock.json` and the source tests. They are not additional
|
|
144
|
+
steps for an installed npm/tarball consumer:
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
npm ci --omit=optional --ignore-scripts
|
|
148
|
+
npm test
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
To prepare a separate local development installation from that checkout, use
|
|
152
|
+
`node bin/abot-socket.mjs setup` and the same [installation guide](INSTALLATION.md).
|
|
153
|
+
Keep its private files outside source control. Tests use dummy identities and
|
|
154
|
+
loopback fixtures; they do not establish live runtime, phone or proxy readiness.
|
package/package.json
CHANGED
|
@@ -1,6 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abot-ai/socket",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"registry": "https://registry.npmjs.org/",
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=24"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"start": "node bin/abot-socket.mjs start",
|
|
14
|
+
"setup": "node bin/abot-socket.mjs setup",
|
|
15
|
+
"check-config": "node bin/abot-socket.mjs check-config",
|
|
16
|
+
"test": "node --test test/*.test.mjs",
|
|
17
|
+
"pair": "node bin/abot-socket.mjs pair",
|
|
18
|
+
"devices": "node bin/abot-socket.mjs devices",
|
|
19
|
+
"revoke": "node bin/abot-socket.mjs revoke",
|
|
20
|
+
"doctor": "node bin/abot-socket.mjs doctor",
|
|
21
|
+
"deployment": "node bin/abot-socket.mjs deployment",
|
|
22
|
+
"check-package": "node scripts/check-package.mjs"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"qrcode-generator": "2.0.4",
|
|
26
|
+
"ws": "8.21.0"
|
|
27
|
+
},
|
|
28
|
+
"description": "Self-hosted authenticated relay for ABot clients and runtimes",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/Dorshitrit/abot-socket.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/Dorshitrit/abot-socket/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/Dorshitrit/abot-socket#readme",
|
|
38
|
+
"files": [
|
|
39
|
+
"bin/abot-socket.mjs",
|
|
40
|
+
"src/main.mjs",
|
|
41
|
+
"src/auth/*.mjs",
|
|
42
|
+
"src/cli/*.mjs",
|
|
43
|
+
"src/config/*.mjs",
|
|
44
|
+
"src/deployment/*.mjs",
|
|
45
|
+
"src/notifications/*.mjs",
|
|
46
|
+
"src/pairing/*.mjs",
|
|
47
|
+
"src/protocol/*.mjs",
|
|
48
|
+
"src/relay/*.mjs",
|
|
49
|
+
"src/review/*.mjs",
|
|
50
|
+
"src/setup/*.mjs",
|
|
51
|
+
"src/transport/*.mjs",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"config.example.json",
|
|
55
|
+
"docs/SELF-HOSTING.md",
|
|
56
|
+
"docs/PROTOCOL.md",
|
|
57
|
+
"docs/SECURITY.md",
|
|
58
|
+
"docs/MIGRATION.md",
|
|
59
|
+
"docs/DEPLOYMENT.md",
|
|
60
|
+
"docs/INSTALLATION.md",
|
|
61
|
+
"docs/APP-REVIEW.md"
|
|
62
|
+
],
|
|
63
|
+
"bin": {
|
|
64
|
+
"abot-socket": "bin/abot-socket.mjs"
|
|
65
|
+
}
|
|
66
|
+
}
|