sporades 0.1.0 โ 0.3.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/CHANGES.md +36 -0
- package/Dockerfile.base +23 -2
- package/README.md +187 -234
- package/bin/sporades-host-helper.js +167 -4
- package/bin/sporades.js +2836 -661
- package/dist/bundle-pipeline.d.ts +4 -0
- package/dist/bundle-pipeline.d.ts.map +1 -1
- package/dist/bundle-pipeline.js +1 -1
- package/dist/bundle-pipeline.js.map +1 -1
- package/dist/cli/cli-help.d.ts +5 -4
- package/dist/cli/cli-help.d.ts.map +1 -1
- package/dist/cli/cli-help.js +16 -1
- package/dist/cli/cli-help.js.map +1 -1
- package/dist/cli/doctor.d.ts +23 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +1320 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/host-helper-release-files.d.ts.map +1 -1
- package/dist/cli/host-helper-release-files.js +4 -0
- package/dist/cli/host-helper-release-files.js.map +1 -1
- package/dist/cli/host-helper-requests.d.ts +12 -2
- package/dist/cli/host-helper-requests.d.ts.map +1 -1
- package/dist/cli/host-request-builders.d.ts.map +1 -1
- package/dist/cli/host-request-builders.js +11 -0
- package/dist/cli/host-request-builders.js.map +1 -1
- package/dist/cli/project-config.d.ts +67 -0
- package/dist/cli/project-config.d.ts.map +1 -0
- package/dist/cli/project-config.js +330 -0
- package/dist/cli/project-config.js.map +1 -0
- package/dist/cli/sporades-host-helper.js +159 -1
- package/dist/cli/sporades-host-helper.js.map +1 -1
- package/dist/cli/sporades.js +291 -116
- package/dist/cli/sporades.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -0
- package/dist/client.js.map +1 -1
- package/dist/server-runtime-source.d.ts +54 -28
- package/dist/server-runtime-source.d.ts.map +1 -1
- package/dist/server-runtime-source.js +544 -188
- package/dist/server-runtime-source.js.map +1 -1
- package/dist/server.d.ts +17 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +48 -1
- package/dist/server.js.map +1 -1
- package/dist/templates/client-runtime-template.d.ts.map +1 -1
- package/dist/templates/client-runtime-template.js +15 -0
- package/dist/templates/client-runtime-template.js.map +1 -1
- package/package.json +8 -1
- package/src/types/client.d.ts +113 -0
- package/src/types/server.d.ts +141 -0
package/CHANGES.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Changes
|
|
2
|
+
|
|
3
|
+
## Unreleased - 2026-07-07
|
|
4
|
+
|
|
5
|
+
Changes since v0.2.0.
|
|
6
|
+
|
|
7
|
+
### ๐ Features
|
|
8
|
+
|
|
9
|
+
- Add doctor hosted capsule checks (5eb39af).
|
|
10
|
+
- Add doctor local runtime checks (36d1d66).
|
|
11
|
+
- Add doctor capsule authoring checks (3561a09).
|
|
12
|
+
- Add doctor project security checks (a474750).
|
|
13
|
+
- Add doctor command envelope (3786aa2).
|
|
14
|
+
|
|
15
|
+
### ๐ Bug Fixes
|
|
16
|
+
|
|
17
|
+
- Fix generated server upload helpers (8d310be).
|
|
18
|
+
|
|
19
|
+
### ๐ Documentation
|
|
20
|
+
|
|
21
|
+
- MD formatting (223cd5c).
|
|
22
|
+
- Harden database transaction coverage (a68beb2).
|
|
23
|
+
- Update roadmap for current/next features (6a72e1d).
|
|
24
|
+
- Plan transaction coverage audit (85a3bcc).
|
|
25
|
+
- Document doctor workflow and roadmap status (ff2eb9c).
|
|
26
|
+
- Plan sporades doctor diagnostics (f52f67a).
|
|
27
|
+
- Update documentation files in the working tree: docs/api/functions/client.createHooks.html, docs/api/functions/client.isAuthenticated.html, docs/api/functions/client.onMessage.html, docs/api/functions/client.sendMessage.html, docs/api/functions/server.Boolean.html, docs/api/functions/server.capsule.html, docs/api/functions/server.Date.html, docs/api/functions/server.endpoint.html, docs/api/functions/server.Json.html, docs/api/functions/server.message.html, docs/api/functions/server.mutation.html, docs/api/functions/server.Number.html, docs/api/functions/server.query.html, docs/api/functions/server.Reference.html, docs/api/functions/server.requireAuth.html, docs/api/functions/server.String.html, docs/api/functions/server.table.html, docs/api/types/client.AppMessage.html, docs/api/types/client.AppMessageStream.html, docs/api/types/client.AuthApi.html, docs/api/types/client.AuthProviders.html, docs/api/types/client.AuthState.html, docs/api/types/client.EmailCredentials.html, docs/api/types/client.FileMetadata.html, docs/api/types/client.FileReference.html, docs/api/types/client.FilesApi.html, docs/api/types/client.HookPrimitives.html, docs/api/types/client.JsonObject.html, docs/api/types/client.JsonValue.html, docs/api/types/client.MutationState.html, docs/api/types/client.PreferencesApi.html, docs/api/types/client.PreferencesResult.html, docs/api/types/client.ProviderState.html, docs/api/types/client.PublicFileUrl.html, docs/api/types/client.PublicUrlOptions.html, docs/api/types/client.QueryState.html, docs/api/types/client.SporadesError.html, docs/api/types/client.SporadesHooks.html, docs/api/types/client.SporadesResult.html, docs/api/types/client.Subscription.html, docs/api/types/client.UploadCompleteEvent.html, docs/api/types/client.UploadOptions.html, docs/api/types/client.UploadProgressEvent.html, docs/api/types/client.UseAuthState.html, docs/api/types/server.AclDatabaseHelpers.html, docs/api/types/server.AclHelpers.html, docs/api/types/server.AclStorageFileMetadata.html, docs/api/types/server.AclStorageHelpers.html, docs/api/types/server.AnyFieldDefinition.html, docs/api/types/server.AuthContext.html, docs/api/types/server.AutoFields.html, docs/api/types/server.Capsule.html, docs/api/types/server.CapsuleContext.html, docs/api/types/server.CapsuleDefinition.html, docs/api/types/server.CapsuleHooks.html, docs/api/types/server.ContextKind.html, docs/api/types/server.ContextMiddleware.html, docs/api/types/server.DatabaseFromSchema.html, docs/api/types/server.EndpointContext.html, docs/api/types/server.EndpointDefinition.html, docs/api/types/server.EndpointHandler.html, docs/api/types/server.EndpointOptions.html, docs/api/types/server.EndpointRequest.html, docs/api/types/server.FieldBuilder.html, docs/api/types/server.FieldDefinition.html, docs/api/types/server.FieldKind.html, docs/api/types/server.FieldValue.html, docs/api/types/server.InsertValues.html, docs/api/types/server.JsonValue.html, docs/api/types/server.Logger.html, docs/api/types/server.MaybePromise.html, docs/api/types/server.MessageApi.html, docs/api/types/server.MessageDefinition.html, docs/api/types/server.MessageHandler.html, docs/api/types/server.MessageScope.html, docs/api/types/server.MiddlewareContext.html, docs/api/types/server.MutationDefinition.html, docs/api/types/server.MutationHandler.html, docs/api/types/server.MutationHook.html, docs/api/types/server.MutationHookEvent.html, docs/api/types/server.MutationResult.html, docs/api/types/server.OrderDirection.html, docs/api/types/server.QueryDefinition.html, docs/api/types/server.QueryHandler.html, docs/api/types/server.ReferenceFieldBuilder.html, docs/api/types/server.ReferenceFieldDefinition.html, docs/api/types/server.RequireAuthOptions.html, docs/api/types/server.RowFromFields.html, docs/api/types/server.SchemaDefinition.html, docs/api/types/server.TableAclContext.html, docs/api/types/server.TableAclOperation.html, docs/api/types/server.TableAclRule.html, docs/api/types/server.TableAclRuleInput.html, docs/api/types/server.TableAclRules.html, docs/api/types/server.TableApi.html, docs/api/types/server.TableDefinition.html, docs/api/types/server.UpdateValues.html, docs/api/variables/client.auth.html, docs/api/variables/client.files.html, docs/api/variables/client.preferences.html.
|
|
28
|
+
|
|
29
|
+
### ๐งช Tests
|
|
30
|
+
|
|
31
|
+
- Audit transaction boundary coverage (f6268ef).
|
|
32
|
+
|
|
33
|
+
### ๐ฆ Packaging
|
|
34
|
+
|
|
35
|
+
- Align scaffold version tests with package version (d3dc529).
|
|
36
|
+
- Clean up npm package tarballs (6303628).
|
package/Dockerfile.base
CHANGED
|
@@ -8,9 +8,30 @@ LABEL com.sporades.base-image.name="sporades-base"
|
|
|
8
8
|
LABEL com.sporades.base-image.version="0.1.0-node22-alpine"
|
|
9
9
|
LABEL com.sporades.base-image.node="22"
|
|
10
10
|
|
|
11
|
-
RUN
|
|
11
|
+
RUN apk add --no-cache openssh-server \
|
|
12
|
+
&& addgroup -g 10001 -S sporades \
|
|
12
13
|
&& adduser -u 10001 -S sporades -G sporades \
|
|
13
|
-
&& mkdir -p /app /app/data \
|
|
14
|
+
&& mkdir -p /app /app/data /run/sshd \
|
|
15
|
+
&& ssh-keygen -A \
|
|
16
|
+
&& printf '%s\n' \
|
|
17
|
+
'#!/bin/sh' \
|
|
18
|
+
'set -eu' \
|
|
19
|
+
'if [ -n "${SPORADES_SSH_AUTHORIZED_KEYS_PATH:-}" ] && [ -s "${SPORADES_SSH_AUTHORIZED_KEYS_PATH}" ]; then' \
|
|
20
|
+
' target="${SPORADES_SSH_AUTHORIZED_KEYS_TARGET:-/app/data/ssh/authorized_keys}"' \
|
|
21
|
+
' ssh_dir="$(dirname "$target")"' \
|
|
22
|
+
' mkdir -p "$ssh_dir"' \
|
|
23
|
+
' cp "${SPORADES_SSH_AUTHORIZED_KEYS_PATH}" "$target"' \
|
|
24
|
+
' if [ ! -s "$ssh_dir/ssh_host_ed25519_key" ]; then' \
|
|
25
|
+
' ssh-keygen -t ed25519 -f "$ssh_dir/ssh_host_ed25519_key" -N "" >/dev/null' \
|
|
26
|
+
' fi' \
|
|
27
|
+
' chmod 700 "$ssh_dir"' \
|
|
28
|
+
' chmod 600 "$target"' \
|
|
29
|
+
' chmod 600 "$ssh_dir/ssh_host_ed25519_key"' \
|
|
30
|
+
' /usr/sbin/sshd -f /etc/ssh/sshd_config -o PasswordAuthentication=no -o PermitRootLogin=no -o AllowUsers=sporades -o AuthorizedKeysFile="$target" -o HostKey="$ssh_dir/ssh_host_ed25519_key" -o PidFile=/tmp/sporades-sshd.pid' \
|
|
31
|
+
'fi' \
|
|
32
|
+
'exec node /app/server.mjs' \
|
|
33
|
+
> /usr/local/bin/sporades-start \
|
|
34
|
+
&& chmod 0755 /usr/local/bin/sporades-start \
|
|
14
35
|
&& chown -R 10001:10001 /app
|
|
15
36
|
|
|
16
37
|
WORKDIR /app
|
package/README.md
CHANGED
|
@@ -1,289 +1,242 @@
|
|
|
1
1
|
# Sporades
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://www.npmjs.com/package/sporades"><img alt="npm version" src="https://img.shields.io/npm/v/sporades.svg"></a>
|
|
5
|
+
<a href="https://www.npmjs.com/package/sporades"><img alt="npm downloads" src="https://img.shields.io/npm/dm/sporades.svg"></a>
|
|
6
|
+
<a href="https://nodejs.org/"><img alt="Node.js >=22" src="https://img.shields.io/badge/node-%3E%3D22-339933?logo=node.js&logoColor=white"></a>
|
|
7
|
+
<a href="https://github.com/mgscox/sporades/blob/main/LICENSE"><img alt="MIT license" src="https://img.shields.io/npm/l/sporades.svg"></a>
|
|
8
|
+
<a href="https://github.com/mgscox/sporades"><img alt="GitHub repository" src="https://img.shields.io/badge/GitHub-mgscox%2Fsporades-181717?logo=github"></a>
|
|
9
|
+
</p>
|
|
4
10
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
- [Roadmap](docs/ROADMAP.md) - candidate features and their promotion status.
|
|
9
|
-
- [Runtime layout](docs/runtime-layout.md) - canonical filesystem paths,
|
|
10
|
-
runtime mounts, and Host server directory structures.
|
|
11
|
-
- [Host server installation](docs/server-installation.md) - preparing a Linux
|
|
12
|
-
Host server for Hosted Capsules.
|
|
13
|
-
|
|
14
|
-
## Auth
|
|
15
|
-
|
|
16
|
-
Sporades apps can enable multiple auth providers in `sporades.json`:
|
|
17
|
-
|
|
18
|
-
```json
|
|
19
|
-
{
|
|
20
|
-
"auth": {
|
|
21
|
-
"providers": {
|
|
22
|
-
"anonymous": true,
|
|
23
|
-
"google": {
|
|
24
|
-
"clientIdEnv": "GOOGLE_CLIENT_ID",
|
|
25
|
-
"clientSecretEnv": "GOOGLE_CLIENT_SECRET"
|
|
26
|
-
},
|
|
27
|
-
"email": true
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
```
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/sporades">[npm package]</a>
|
|
13
|
+
</p>
|
|
32
14
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
sessions, and `auth.mode: "google"` enables Google alongside anonymous sessions.
|
|
15
|
+
Designed as Agents-first, Sporades is a CLI-first platform for building, running, inspecting, and hosting
|
|
16
|
+
small full-stack web Capsules.
|
|
36
17
|
|
|
37
|
-
|
|
18
|
+
It is intended for developers and coding Agents who want deterministic commands
|
|
19
|
+
instead of dashboards: scaffold a Capsule, run it locally, test the bundled
|
|
20
|
+
runtime in Docker, then publish it to a private or cloud Host server.
|
|
38
21
|
|
|
39
22
|
```sh
|
|
40
|
-
|
|
23
|
+
# install sporades globally
|
|
24
|
+
npm install -g sporades
|
|
25
|
+
sporades -h
|
|
41
26
|
```
|
|
42
27
|
|
|
43
|
-
|
|
28
|
+
Create a 'todo' app and spin-up a live-refresh dev server, a staging server on local docker service, and deploy to a remote production server:
|
|
44
29
|
|
|
45
30
|
```sh
|
|
46
|
-
sporades
|
|
31
|
+
sporades create notes --template todo
|
|
32
|
+
cd notes
|
|
33
|
+
sporades dev
|
|
34
|
+
sporades deploy
|
|
35
|
+
sporades host push --restart
|
|
47
36
|
```
|
|
48
37
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
## Why Sporades?
|
|
39
|
+
|
|
40
|
+
- **One command loop for humans and agents**: create, run, inspect, deploy, and
|
|
41
|
+
repair Capsules from the terminal.
|
|
42
|
+
- **Full-stack without the usual wiring**: server schema, queries, mutations,
|
|
43
|
+
auth, file uploads, custom endpoints, app messages, and browser hooks live
|
|
44
|
+
behind a small authoring API.
|
|
45
|
+
- **Same bundle everywhere**: Dev sessions, local Docker Container sessions, and
|
|
46
|
+
Hosted Capsules all run the same bundled server and client output.
|
|
47
|
+
- **Private-host friendly**: Host servers use boring, inspectable pieces:
|
|
48
|
+
Docker, Caddy, Node, SQLite, filesystem storage, and SSH.
|
|
49
|
+
- **Agent-operable by default**: structured JSON output, actionable error hints,
|
|
50
|
+
logs, database inspection, and non-interactive commands are first-class.
|
|
51
|
+
- **Comprehensive SDK**: built-in authentication, database, user preferences,
|
|
52
|
+
and storage.
|
|
53
|
+
- **Realtime everything**: configuration free - automatic notifications, even for Postgress and AWS-S3
|
|
54
|
+
|
|
55
|
+
## Documentation
|
|
56
|
+
|
|
57
|
+
- [User guide](docs/user-guide.md): build, run, inspect, deploy, auth,
|
|
58
|
+
preferences, files, endpoints, messages, and common workflows.
|
|
59
|
+
- [Architecture](docs/architecture.md): platform model, runtime modes, Host
|
|
60
|
+
server design, and ownership boundaries.
|
|
61
|
+
- [Runtime layout](docs/runtime-layout.md): generated files, mounts, Host
|
|
62
|
+
directories, and persistent data locations.
|
|
63
|
+
- [Host server installation](docs/server-installation.md): prepare a Linux Host
|
|
64
|
+
server for Hosted Capsules.
|
|
65
|
+
- [Product requirements](docs/PRD.md): implemented scope, deferred scope, and
|
|
66
|
+
core product principles.
|
|
67
|
+
- [Roadmap](docs/ROADMAP.md): candidate features and promotion status.
|
|
52
68
|
|
|
53
|
-
|
|
54
|
-
without printing OAuth client IDs or secrets.
|
|
69
|
+
## A Tiny Capsule - the entire server code for a real-time 'to-do' app
|
|
55
70
|
|
|
56
|
-
|
|
71
|
+
```ts
|
|
72
|
+
// Everything we need comes from Sporades server SDK
|
|
73
|
+
import {
|
|
74
|
+
Boolean,
|
|
75
|
+
String,
|
|
76
|
+
capsule,
|
|
77
|
+
mutation,
|
|
78
|
+
query,
|
|
79
|
+
table,
|
|
80
|
+
} from "sporades/server";
|
|
81
|
+
|
|
82
|
+
export default capsule({
|
|
83
|
+
name: "notes",
|
|
84
|
+
|
|
85
|
+
// tables automatically get createdAt, createdBy, and a unique Id
|
|
86
|
+
schema: {
|
|
87
|
+
// tables can have optional ACL - default is "open to all"
|
|
88
|
+
todos: table({
|
|
89
|
+
text: String(),
|
|
90
|
+
done: Boolean().default(false),
|
|
91
|
+
ownerId: String(),
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// queries are automatically write-through cached on the server
|
|
96
|
+
queries: {
|
|
97
|
+
// all todo entries for current user (anonymous by default)
|
|
98
|
+
todos: query((ctx) =>
|
|
99
|
+
// context 'ctx' contains user, db, storage, auth, etc.
|
|
100
|
+
ctx.db.todos
|
|
101
|
+
.where("ownerId", ctx.auth.userId)
|
|
102
|
+
.orderBy("createdAt", "desc")
|
|
103
|
+
.all(),
|
|
104
|
+
),
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
mutations: {
|
|
108
|
+
addTodo: mutation((ctx, text: string) =>
|
|
109
|
+
ctx.db.todos.insert({ text, ownerId: ctx.auth.userId }),
|
|
110
|
+
),
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Client scaffolds wire `sporades/client` into React or Preact hooks so the UI can
|
|
116
|
+
subscribe to queries and run mutations without hand-rolled fetch plumbing.
|
|
117
|
+
|
|
118
|
+
## What You Get
|
|
119
|
+
|
|
120
|
+
Sporades currently includes:
|
|
121
|
+
|
|
122
|
+
- React and Preact project scaffolds with `blank`, `todo`, `guestbook`, and
|
|
123
|
+
`photo-library` templates.
|
|
124
|
+
- Local Dev sessions with rebuilds, WebSocket reconnects, SQLite persistence,
|
|
125
|
+
logs, database inspection, auth helpers, and file storage.
|
|
126
|
+
- Local Docker Container sessions for production-like staging tests.
|
|
127
|
+
- Hosted Capsules on SSH-reachable Host servers, routed through Caddy and run in
|
|
128
|
+
hardened Docker containers.
|
|
129
|
+
- Runtime-owned auth with anonymous sessions, email auth, Google OAuth, and
|
|
130
|
+
local identity simulation for tests and agents.
|
|
131
|
+
- Runtime-owned current-user preferences exposed through `sporades/client`
|
|
132
|
+
without app preference tables. Preferences follow the Sporades user identity,
|
|
133
|
+
survive Anonymous account linking, and notify same-user connected clients when
|
|
134
|
+
they change.
|
|
135
|
+
- File uploads with private reads, explicit public URLs, replacement versions,
|
|
136
|
+
and local MinIO-backed storage support.
|
|
137
|
+
- Custom HTTP endpoints and app messages over the Sporades client transport.
|
|
138
|
+
- Sealed Server env for public/private key encrypted server-only environemt variables, keeping them out of browser bundles and
|
|
139
|
+
release archives.
|
|
140
|
+
|
|
141
|
+
File reads stay behind Sporades HTTP routes. They are not filesystem paths or presigned MinIO/S3 URLs.
|
|
142
|
+
Capsules can opt into local MinIO storage with
|
|
143
|
+
`services.storage.engine: "minio"`; omitted upload paths use the file name in
|
|
144
|
+
the Default File bucket, falling back to the logical `/default/upload` File path when no file name exists.
|
|
145
|
+
The Default File bucket is only a namespace fallback, not a user bucket or policy boundary.
|
|
146
|
+
|
|
147
|
+
## Install
|
|
148
|
+
|
|
149
|
+
Sporades requires Node.js 22 or newer. Install the CLI from npm:
|
|
57
150
|
|
|
58
151
|
```sh
|
|
59
|
-
|
|
152
|
+
npm install --global sporades
|
|
153
|
+
sporades --help
|
|
60
154
|
```
|
|
61
155
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
156
|
+
Sporades depends on `esbuild` for Capsule bundling. On newer npm versions, you
|
|
157
|
+
may see an `allow-scripts` warning during global install because `esbuild` uses
|
|
158
|
+
a postinstall script to select the native binary for your platform.
|
|
65
159
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`localStorage`:
|
|
160
|
+
If npm blocks that script and bundling later fails, reinstall while explicitly
|
|
161
|
+
allowing the `esbuild` install script:
|
|
69
162
|
|
|
70
163
|
```sh
|
|
71
|
-
|
|
164
|
+
npm install --global sporades --allow-scripts=esbuild
|
|
72
165
|
```
|
|
73
166
|
|
|
74
|
-
|
|
75
|
-
connected browser client, `--client all` to push it into every connected
|
|
76
|
-
browser client for the app, or `--client <id>` with an ID from
|
|
77
|
-
`sporades auth clients --json` to target one exact browser client:
|
|
167
|
+
To approve `esbuild` persistently for your user npm config:
|
|
78
168
|
|
|
79
169
|
```sh
|
|
80
|
-
|
|
170
|
+
npm config set allow-scripts=esbuild --location=user
|
|
81
171
|
```
|
|
82
172
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
updated. The `localStorage` payload is always returned as a manual fallback even
|
|
87
|
-
when no browser client is connected. This is local identity simulation only: it
|
|
88
|
-
creates a Sporades-owned dev session for `ctx.auth` testing and does not
|
|
89
|
-
complete OAuth, trust arbitrary JWTs, or authenticate against a real provider.
|
|
90
|
-
`google` can be used as a simulated provider shape for browser tests that need
|
|
91
|
-
Google-like auth metadata. Simulated sessions use the same session lifetime as
|
|
92
|
-
normal Sporades sessions.
|
|
93
|
-
|
|
94
|
-
Sporades session records store creation and expiry metadata. Sessions expire 30
|
|
95
|
-
days after they are created or refreshed. Missing, invalid, or expired session
|
|
96
|
-
tokens resolve to a fresh anonymous session instead of authenticating the old
|
|
97
|
-
user forever. Email sign-up links the current anonymous account and rotates the
|
|
98
|
-
session token; email sign-in rotates the current anonymous token onto the email
|
|
99
|
-
account. Google sign-in links through the server-owned OAuth callback and
|
|
100
|
-
refreshes the current session token's expiry, because the redirect callback does
|
|
101
|
-
not expose a safe client token handoff point.
|
|
102
|
-
|
|
103
|
-
After running `sporades auth set <provider>`, restart any running dev session so
|
|
104
|
-
the server runtime reloads the updated Server env and auth configuration:
|
|
173
|
+
When working in this repository, plain `npm install` uses the project
|
|
174
|
+
`allowScripts` entry in `package.json` to approve the reviewed `esbuild`
|
|
175
|
+
install script. If `esbuild` is upgraded, review and refresh that approval with:
|
|
105
176
|
|
|
106
177
|
```sh
|
|
107
|
-
|
|
108
|
-
sporades dev
|
|
178
|
+
npm approve-scripts esbuild
|
|
109
179
|
```
|
|
110
180
|
|
|
111
|
-
|
|
112
|
-
|
|
181
|
+
Generated Capsules use the same `sporades` command for local Dev sessions,
|
|
182
|
+
Container sessions, inspection, and Host operations.
|
|
113
183
|
|
|
114
|
-
```tsx
|
|
115
|
-
import { auth } from "sporades/client";
|
|
116
184
|
|
|
117
|
-
|
|
118
|
-
email: "mira@example.com",
|
|
119
|
-
password: "correct horse battery staple",
|
|
120
|
-
name: "Mira",
|
|
121
|
-
});
|
|
185
|
+
## Quick Start
|
|
122
186
|
|
|
123
|
-
|
|
124
|
-
email: "mira@example.com",
|
|
125
|
-
password: "correct horse battery staple",
|
|
126
|
-
});
|
|
187
|
+
Create a Capsule:
|
|
127
188
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
console.error(signIn.error?.message, signIn.error?.hint);
|
|
132
|
-
}
|
|
189
|
+
```sh
|
|
190
|
+
sporades create notes --template todo
|
|
191
|
+
cd notes
|
|
133
192
|
```
|
|
134
193
|
|
|
135
|
-
|
|
136
|
-
redirect flow:
|
|
194
|
+
Run a local Dev session:
|
|
137
195
|
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Client code signs out through the same auth surface. A successful sign-out
|
|
143
|
-
clears the stored Sporades session token and refreshes auth state to a fresh
|
|
144
|
-
anonymous session, so apps can route immediately:
|
|
145
|
-
|
|
146
|
-
```tsx
|
|
147
|
-
import { auth } from "sporades/client";
|
|
148
|
-
|
|
149
|
-
async function signOutAndGoHome(navigate: (path: string) => void) {
|
|
150
|
-
const result = await auth.signOut();
|
|
151
|
-
if (result.data?.ok) {
|
|
152
|
-
navigate("/");
|
|
153
|
-
} else {
|
|
154
|
-
console.error(result.error?.message);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
196
|
+
```sh
|
|
197
|
+
sporades dev
|
|
157
198
|
```
|
|
158
199
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
Client code uses `files.upload()` from `sporades/client` to upload browser
|
|
162
|
-
`File` or `Blob` values. The SDK first asks the Sporades server for an upload
|
|
163
|
-
URL, transfers the bytes internally, and resolves with Sporades-owned metadata:
|
|
164
|
-
file ID, absolute File path, original filename, MIME type, size, bucket, and
|
|
165
|
-
version. Pass `path: "/images/avatar.png"` to choose an absolute Capsule-scoped
|
|
166
|
-
File path; omitted paths use the browser file name in the Default File bucket,
|
|
167
|
-
falling back to the logical `/default/upload` File path when no file name
|
|
168
|
-
exists. Arrays are accepted as a convenience and are uploaded sequentially.
|
|
169
|
-
|
|
170
|
-
Uploaded bytes are private by default. Ownership and privacy are runtime file
|
|
171
|
-
metadata and ACL behavior; the Default File bucket is only a namespace fallback,
|
|
172
|
-
not a user bucket or policy boundary. Local filesystem storage is the default:
|
|
173
|
-
during Dev sessions, bytes live under `.sporades/files/`; Container sessions
|
|
174
|
-
use the mounted `/app/data` volume. Capsules can declare
|
|
175
|
-
`services.storage.engine: "minio"` to use MinIO as local Capsule service
|
|
176
|
-
storage while keeping the same `files` SDK calls. Metadata, buckets, upload
|
|
177
|
-
records, and public URL records live in the database either way.
|
|
178
|
-
|
|
179
|
-
Private reads go through `files.url(fileReference)` or
|
|
180
|
-
`files.download(fileReference)`, where the File reference is either a File ID or
|
|
181
|
-
an absolute File path.
|
|
182
|
-
Public reads must be created explicitly with exactly one expiry choice:
|
|
183
|
-
`ttlSeconds`, `expires`, or `noExpiry: true`. Replacing a file preserves the file
|
|
184
|
-
ID, creates a new version, and invalidates previously generated private and
|
|
185
|
-
public URLs. Uploading to an existing live File path overwrites that file while
|
|
186
|
-
preserving its File ID; deleting a file frees the path for a later upload with a
|
|
187
|
-
new File ID. Public and private read URLs are Sporades HTTP routes, not
|
|
188
|
-
filesystem paths or presigned MinIO/S3 URLs.
|
|
189
|
-
|
|
190
|
-
## Endpoint handlers
|
|
191
|
-
|
|
192
|
-
Capsules can register custom HTTP endpoints with `endpoint({ method, path }, handler)`.
|
|
193
|
-
The handler receives the same server-owned context concepts as queries and
|
|
194
|
-
mutations:
|
|
200
|
+
Inspect it from another terminal:
|
|
195
201
|
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
body: {
|
|
201
|
-
method: ctx.request.method,
|
|
202
|
-
path: ctx.request.path,
|
|
203
|
-
query: ctx.request.query,
|
|
204
|
-
body: ctx.request.body,
|
|
205
|
-
userId: ctx.auth.userId,
|
|
206
|
-
},
|
|
207
|
-
}));
|
|
202
|
+
```sh
|
|
203
|
+
sporades logs
|
|
204
|
+
sporades db list
|
|
205
|
+
sporades db dump --json
|
|
208
206
|
```
|
|
209
207
|
|
|
210
|
-
|
|
211
|
-
`ctx.request` exposes `method`, `path`, lower-cased `headers`, query parameters,
|
|
212
|
-
and parsed request body data. Structured endpoint responses use
|
|
213
|
-
`{ status, headers, body }`; object bodies are returned as JSON, and string bodies
|
|
214
|
-
are returned as text.
|
|
208
|
+
Test the bundled Capsule in Docker:
|
|
215
209
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Capsules can declare ephemeral app-level messages with `message(handler)` and
|
|
219
|
-
clients can send them through `sporades/client` without touching raw WebSocket
|
|
220
|
-
objects:
|
|
221
|
-
|
|
222
|
-
```ts
|
|
223
|
-
// server/index.ts
|
|
224
|
-
import { capsule, message } from "sporades/server";
|
|
225
|
-
|
|
226
|
-
export default capsule({
|
|
227
|
-
messages: {
|
|
228
|
-
typing: message((ctx, data) => {
|
|
229
|
-
const sentToClients = ctx.messages.send({ type: "typing", data });
|
|
230
|
-
return { ok: true, sentToClients };
|
|
231
|
-
}),
|
|
232
|
-
},
|
|
233
|
-
});
|
|
210
|
+
```sh
|
|
211
|
+
sporades deploy
|
|
234
212
|
```
|
|
235
213
|
|
|
236
|
-
|
|
237
|
-
// client/index.tsx
|
|
238
|
-
import { onMessage, sendMessage } from "sporades/client";
|
|
214
|
+
Add a Host profile and publish when you have a Host server ready:
|
|
239
215
|
|
|
240
|
-
|
|
216
|
+
```sh
|
|
217
|
+
sporades host add personal \
|
|
218
|
+
--server user@example.com \
|
|
219
|
+
--domain apps.example.com
|
|
241
220
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
console.log(message.data);
|
|
246
|
-
});
|
|
221
|
+
sporades host bootstrap --host personal --json
|
|
222
|
+
sporades host register notes --host personal --json
|
|
223
|
+
sporades host push --host personal --subname notes --verify --json
|
|
247
224
|
```
|
|
248
225
|
|
|
249
|
-
|
|
250
|
-
namespaces such as `app.`, `auth.`, `query.`, `mutation.`, `files.`, and
|
|
251
|
-
`runtime.` for internal transport messages. Client-origin app messages always
|
|
252
|
-
run through declared Capsule handlers; the platform does not directly relay
|
|
253
|
-
arbitrary client messages. `ctx.messages.send()` defaults to the current user's
|
|
254
|
-
connected clients, and handlers can explicitly target users with
|
|
255
|
-
`{ scope: "users", userIds: [...] }`. It returns the number of connected clients
|
|
256
|
-
the message was sent to. App-wide `{ scope: "all" }` broadcasts are not
|
|
257
|
-
available from client-origin handlers.
|
|
226
|
+
## Project Status
|
|
258
227
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
228
|
+
Sporades is early, active platform work. It is useful for fast prototypes,
|
|
229
|
+
agent-driven app loops, local production-like testing, and private hosted
|
|
230
|
+
Capsules, but it is not trying to be a full production platform (yet). Check the
|
|
231
|
+
[Roadmap](docs/ROADMAP.md) for in-flight and planned features.
|
|
262
232
|
|
|
263
|
-
|
|
233
|
+
The current focus is keeping the authoring surface small, the runtime
|
|
234
|
+
inspectable, and the operational path friendly to both developers and agents.
|
|
235
|
+
Container SSH access is opt-in for local Container sessions and Hosted
|
|
236
|
+
Capsules through authorized public keys in `sporades.json`. Normal management
|
|
237
|
+
remains the CLI and Host helper surfaces; SSH is a compatibility and emergency
|
|
238
|
+
access path.
|
|
264
239
|
|
|
265
|
-
|
|
266
|
-
function receives the current `ctx` and can return an enriched context for the
|
|
267
|
-
request handler, query, mutation, or mutation hook:
|
|
268
|
-
|
|
269
|
-
```ts
|
|
270
|
-
export default capsule({
|
|
271
|
-
middleware: [
|
|
272
|
-
(ctx) => ({ ...ctx, tenant: ctx.env.TENANT }),
|
|
273
|
-
(ctx) => {
|
|
274
|
-
if (!ctx.tenant) {
|
|
275
|
-
throw Object.assign(new Error("Missing tenant."), {
|
|
276
|
-
hint: "Set TENANT in .env.sporades.server.",
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
return ctx;
|
|
280
|
-
},
|
|
281
|
-
],
|
|
282
|
-
});
|
|
283
|
-
```
|
|
240
|
+
## License
|
|
284
241
|
|
|
285
|
-
|
|
286
|
-
HTTP endpoint request. Functions run in the order they appear in the array, and
|
|
287
|
-
each function receives the context returned by the previous function. Middleware
|
|
288
|
-
receives `ctx.kind` as `"query"`, `"mutation"`, or `"endpoint"`. Throw an error
|
|
289
|
-
with a `hint` property to block the request with a structured Sporades error.
|
|
242
|
+
MIT
|