Skip to content

Commit b13c43b

Browse files
docs: add features/app-install page for jss install (#20)
Covers Phases 1+2+4+6 of JSS#464 — bare name / org/repo / URL / refs / renames, bearer + NIP-98 auth, --pod targeting, --bundle (with the JSON-LD shape + the solid-apps/bundles repo), how it works under the hood, and a troubleshooting table. Sidebar entry added under Features, next to git-integration. Fixes #19
1 parent 12ffaa5 commit b13c43b

2 files changed

Lines changed: 200 additions & 0 deletions

File tree

docs/features/app-install.md

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
---
2+
sidebar_position: 14
3+
title: Installing Apps
4+
description: Install Solid apps into your pod with one command — jss install
5+
---
6+
7+
# Installing Apps
8+
9+
JSS ships a built-in `install` subcommand that pulls a Solid app from a git repo and pushes it into your running pod at `/public/apps/<name>/`. One command, no clone-and-push dance, no token plumbing — the hard parts (git auto-init, ACL-gated push, working-tree extraction via `updateInstead`) are handled by the same git HTTP backend JSS already uses.
10+
11+
## Quick start
12+
13+
```bash
14+
jss start --provision-keys & # pod running on http://localhost:4443
15+
jss install chrome # installs solid-apps/chrome → /public/apps/chrome/
16+
```
17+
18+
Open `http://localhost:4443/public/apps/chrome/` in a browser. That's it.
19+
20+
## App specs
21+
22+
The argument to `install` accepts five forms:
23+
24+
| Input | Resolves to | Pod path |
25+
|---|---|---|
26+
| `chrome` | `github.com/solid-apps/chrome` (default registry) | `/public/apps/chrome/` |
27+
| `JavaScriptSolidServer/git` | `github.com/JavaScriptSolidServer/git` | `/public/apps/git/` |
28+
| `https://github.com/foo/bar` | as-is | `/public/apps/bar/` |
29+
| `chrome#v1` | `github.com/solid-apps/chrome` at ref `v1` | `/public/apps/chrome/` |
30+
| `litecut/litecut.github.io=litecut` | `github.com/litecut/litecut.github.io`, renamed | `/public/apps/litecut/` |
31+
32+
Two optional suffixes apply to any form:
33+
34+
- **`#<branch-or-tag>`** — pin a ref. Uses `git clone --branch <ref>` under the hood.
35+
- **`=<name>`** — override the pod-path name. Useful when the repo's last segment isn't what you want under `/public/apps/`.
36+
37+
Multiple specs in one command:
38+
39+
```bash
40+
jss install chrome vellum pdf hub
41+
```
42+
43+
Each is installed independently; per-app `` / `` / `` status, exit non-zero if any failed.
44+
45+
## Authentication
46+
47+
The install needs write access on the target pod. Two paths:
48+
49+
### Bearer token (default)
50+
51+
```bash
52+
jss install chrome --user me --password me
53+
# or via env (keeps the secret out of shell history):
54+
JSS_SINGLE_USER_PASSWORD=secret jss install chrome
55+
```
56+
57+
`POST <pod>/idp/credentials` returns a token, which is sent as `Authorization: Bearer ...` on each push.
58+
59+
If the pod runs in `--public` mode (no IDP), no token is fetched; writes are unauthenticated.
60+
61+
### Nostr (NIP-98)
62+
63+
```bash
64+
jss install chrome --nostr-privkey <64-hex>
65+
# or via env:
66+
NOSTR_PRIVKEY=<64-hex> jss install chrome
67+
```
68+
69+
Each push is signed with a NIP-98 event (Schnorr signature on a `kind: 27235` Nostr event). JSS verifies the signature, derives a `did:nostr:<pubkey>` identity, and runs WAC against that.
70+
71+
Pairs naturally with `--provision-keys`: the privkey JSS auto-generates at `<pod>/private/privkey.jsonld` is the natural source.
72+
73+
```bash
74+
jss start --provision-keys &
75+
PRIVKEY=$(jq -r .secretKeyMultibase pod-data/private/privkey.jsonld | sed 's/^f8126//')
76+
NOSTR_PRIVKEY=$PRIVKEY jss install chrome
77+
```
78+
79+
ACL on the target path must grant the corresponding pubkey:
80+
81+
```turtle
82+
<#owner> a acl:Authorization;
83+
acl:agent <did:nostr:59427bb1...>;
84+
acl:accessTo <./>;
85+
acl:default <./>;
86+
acl:mode acl:Read, acl:Write, acl:Control.
87+
```
88+
89+
This is typically already true on a `--provision-keys` pod — JSS seeds the owner ACL to grant the provisioned key.
90+
91+
## Targeting a different pod
92+
93+
```bash
94+
jss install chrome --pod http://192.168.1.10:5544
95+
```
96+
97+
Default is `http://localhost:4443`. Auth flags apply against the chosen pod.
98+
99+
## Bundles
100+
101+
`--bundle <source>` installs a set of apps from a JSON-LD manifest. Same auth, same target, same per-app status.
102+
103+
```bash
104+
jss install --bundle starter # solid-apps/bundles/HEAD/starter.jsonld
105+
jss install --bundle media chrome # bundle + ad-hoc additions
106+
jss install --bundle ./my-stack.jsonld # local file
107+
jss install --bundle https://my.pod/bundles/dev.jsonld
108+
```
109+
110+
### Source resolution
111+
112+
| Input | Resolves to |
113+
|---|---|
114+
| `--bundle starter` | `https://raw.githubusercontent.com/solid-apps/bundles/HEAD/starter.jsonld` |
115+
| `--bundle <org>/<repo>` | `https://raw.githubusercontent.com/<org>/<repo>/HEAD/bundle.jsonld` |
116+
| `--bundle https://...` | fetch as-is |
117+
| `--bundle ./path.jsonld` | local filesystem (absolute paths supported) |
118+
119+
`/HEAD/` resolves to the repo's default branch — works for both `gh-pages`-default repos (solid-apps convention) and `main`-default repos.
120+
121+
### Bundle format
122+
123+
JSON-LD `schema:ItemList`:
124+
125+
```json
126+
{
127+
"@context": { "schema": "https://schema.org/", "app": "urn:jss:app:" },
128+
"@id": "#bundle",
129+
"@type": "schema:ItemList",
130+
"schema:name": "Starter",
131+
"schema:description": "Minimal pleasant first-run set",
132+
"schema:itemListElement": [
133+
"chrome",
134+
"vellum",
135+
{ "app:spec": "litecut/litecut.github.io=litecut", "app:label": "Litecut" }
136+
]
137+
}
138+
```
139+
140+
Each item is either:
141+
142+
- A **bare string** — any spec `jss install` accepts
143+
- An **object** — required `app:spec`, optional `app:label` / `app:description` for UI tooling
144+
145+
### Curated bundles
146+
147+
The [`solid-apps/bundles`](https://github.com/solid-apps/bundles) repo hosts ready-made bundles:
148+
149+
| Bundle | Apps |
150+
|---|---|
151+
| `starter` | chrome, vellum, pdf, alarm |
152+
| `all` | chrome, vellum, win98, pdf, hub, alarm, playlist |
153+
| `media` | playlist, pdf |
154+
| `productivity` | vellum, hub, win98 |
155+
156+
```bash
157+
jss install --bundle starter
158+
```
159+
160+
### Sharing custom bundles
161+
162+
Bundles are JSON-LD documents — they live anywhere a JSON-LD doc can. Host yours on your pod, in a GitHub repo, or any static server:
163+
164+
```bash
165+
jss install --bundle https://my.pod/bundles/dev-stack.jsonld
166+
```
167+
168+
ACL-gated, version-controlled (if in git), pointable from a single URL. The Linux-distribution analogy is apt: `apt install task-server` becomes `jss install --bundle task-server`, but the manifests are sharable Solid resources instead of fixed-path config files.
169+
170+
## How it works
171+
172+
Under the hood, `jss install <name>` is:
173+
174+
1. **Resolve** the spec to a source URL (`github.com/solid-apps/chrome` for bare names).
175+
2. **Authenticate.** Fetch a bearer token from `<pod>/idp/credentials`, OR build a NIP-98 signed event if `--nostr-privkey` is set. Skipped entirely if the pod is in `--public` mode.
176+
3. **Clone** the repo to a temp directory. Full clone — no `--depth`, because shallow pushes are rejected by `git-receive-pack`.
177+
4. **Dual push** to `<pod>/public/apps/<name>` on both `HEAD:main` and `HEAD:gh-pages`. JSS auto-inits the destination repo, and whichever ref matches the server-side HEAD triggers `receive.denyCurrentBranch updateInstead` to extract the working tree onto disk where JSS serves it as static resources. The other ref is a harmless stranded reference.
178+
5. **Clean up** the temp directory.
179+
180+
Idempotent on re-run. Skip-on-existing-non-repo paths (e.g. jspod's bundled `pilot`) report a friendly `⊘ skipped` instead of an error.
181+
182+
## Troubleshooting
183+
184+
| Symptom | Cause | Fix |
185+
|---|---|---|
186+
| `✗ <name>: invalid app name "..."` | Spec doesn't match `/^[a-z0-9][a-z0-9_.-]*$/i` (bare-name form) or `<org>/<repo>` | Check the spec; review the [App specs](#app-specs) table |
187+
| `✗ <name>: clone failed: Repository not found` | The repo doesn't exist at the resolved URL | Verify the source — `github.com/solid-apps/<name>` for bare names |
188+
| `✗ <name>: push failed: shallow update not allowed` | Shouldn't happen with this tool — but the symptom on a manual clone-and-push is using `--depth=1` | Remove `--depth` from the clone |
189+
| `✗ <name>: push failed: HTTP 401` | Auth failed | Check `--user` / `--password`; for Nostr, check that the ACL grants the pubkey |
190+
| `✗ <name>: push failed: HTTP 413` | Body exceeds JSS's `bodyLimit` (10 MB) | Tracked as [JSS#474](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/issues/474). Workaround: install a smaller repo or push a no-history snapshot |
191+
| Working tree empty after push (`/public/apps/<name>/index.html` returns 404) | Server-side HEAD doesn't match the pushed branch | JSS 0.0.197+ pins `-b main` on auto-init; upgrade if you see this |
192+
| `⊘ <name>: skipped (path already in use)` | The target path has content but no `.git/` (e.g. jspod's bundled `pilot`) | Expected — JSS refuses to clobber non-repo content |
193+
194+
## See also
195+
196+
- [Git Integration](./git-integration.md) — the substrate that powers `install`
197+
- [`jss install --help`](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/blob/gh-pages/src/cli/install.js) — the source
198+
- [solid-apps/bundles](https://github.com/solid-apps/bundles) — curated bundle repo
199+
- [Phased plan (JSS#464)](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/issues/464) — the design roadmap; Phases 3 (`--did`) and 5 (curated no-arg default) still ahead

sidebars.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const sidebars: SidebarsConfig = {
4141
'features/live-reload',
4242
'features/mashlib-ui',
4343
'features/git-integration',
44+
'features/app-install',
4445
'features/activitypub',
4546
'features/nostr',
4647
'features/e2ee',

0 commit comments

Comments
 (0)