Show DATUM login credentials and document complete app launch requirements
This commit is contained in:
@@ -91,3 +91,5 @@ Adding a new app requires updates in multiple places:
|
|||||||
## Port Assignments
|
## Port Assignments
|
||||||
|
|
||||||
See [PORTS.md](./PORTS.md) for complete mapping. Dev ports are offset by +10000.
|
See [PORTS.md](./PORTS.md) for complete mapping. Dev ports are offset by +10000.
|
||||||
|
|
||||||
|
Before submitting an app, complete **Launch acceptance: credentials, signer, and HTTP nodes** in `docs/app-developer-guide.md`. A generated password needs an authenticated credential interstitial; native Nostr login needs a tested first-launch chooser. Container health alone is not launch acceptance.
|
||||||
|
|||||||
@@ -1892,6 +1892,26 @@ autopilot.active=false\n",
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app_id == "datum" {
|
||||||
|
// This is the same platform-owned secret injected into DATUM and
|
||||||
|
// Gashboard. Never publish it in the manifest or a UI fallback.
|
||||||
|
let password = tokio::fs::read_to_string(
|
||||||
|
self.config.data_dir.join("secrets/datum-admin-password"),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("DATUM credentials are not available yet; wait for installation to finish")?;
|
||||||
|
let password = password.trim();
|
||||||
|
anyhow::ensure!(!password.is_empty(), "DATUM administrator password is empty");
|
||||||
|
return Ok(serde_json::json!({
|
||||||
|
"title": "DATUM Gateway login",
|
||||||
|
"description": "Use this password when DATUM asks you to unlock configuration. In Config, set your Bitcoin payout address. Point miners at this node's IP address on Stratum port 23334 (stratum+tcp://NODE-IP:23334). Gashboard connects automatically.",
|
||||||
|
"credentials": [
|
||||||
|
{ "label": "Username", "value": "admin" },
|
||||||
|
{ "label": "Password", "value": password, "sensitive": true }
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
if app_id == "photoprism" {
|
if app_id == "photoprism" {
|
||||||
return Ok(serde_json::json!({
|
return Ok(serde_json::json!({
|
||||||
"title": "PhotoPrism credentials",
|
"title": "PhotoPrism credentials",
|
||||||
|
|||||||
@@ -789,3 +789,45 @@ adapter instead of reporting a successful installation with no usable backend.
|
|||||||
For example, Angor Indexer requires `mempool-api` (shown to users as its owning
|
For example, Angor Indexer requires `mempool-api` (shown to users as its owning
|
||||||
Mempool app), shares that index and declares only an `api` interface. API-only
|
Mempool app), shares that index and declares only an `api` interface. API-only
|
||||||
interfaces belong in Services and do not generate browser launch buttons.
|
interfaces belong in Services and do not generate browser launch buttons.
|
||||||
|
|
||||||
|
|
||||||
|
## Launch acceptance: credentials, signer, and HTTP nodes
|
||||||
|
|
||||||
|
An app is not ready just because its container is healthy. Before submission,
|
||||||
|
verify its first launch from My Apps, app details, a browser tab and Companion:
|
||||||
|
|
||||||
|
- Declare a real UI interface and stage the app icon in the web UI assets. Check
|
||||||
|
the installing tile as well as the completed installation: a UI app belongs
|
||||||
|
in My Apps and must not appear as an iconless service.
|
||||||
|
- If the app needs a password or first-run token, provide the shared credential
|
||||||
|
interstitial **before** launch, with copy controls and setup instructions.
|
||||||
|
Generating a secret in the manifest does not register this screen. Implement
|
||||||
|
`package.credentials` in `core/archipelago/src/api/rpc/package/install.rs`
|
||||||
|
and register the app in `CREDENTIAL_INTERSTITIAL_APPS` in
|
||||||
|
`neode-ui/src/stores/appLauncher.ts`. Both changes require a platform update;
|
||||||
|
app-only sideloads cannot add this RPC integration. File Browser and DATUM
|
||||||
|
are examples. Read generated secrets from the node's configured data directory;
|
||||||
|
never put them in a manifest, static browser bundle, default-password fallback,
|
||||||
|
logs, screenshots, or test reports. Keep the RPC dashboard-authenticated.
|
||||||
|
- Explain initial configuration and client connection details. For DATUM this
|
||||||
|
includes its administrator password, Bitcoin payout address, and the node's
|
||||||
|
Stratum address on port 23334. App-to-app connections use container DNS
|
||||||
|
(`http://datum:7152`), never a container IP address.
|
||||||
|
- Native Nostr apps should open the host identity chooser once on an explicit
|
||||||
|
app launch when unauthenticated, then finish the app's ordinary NIP-07 login.
|
||||||
|
The app may call `archipelagoNostr.selectIdentity()` at initial mount for this
|
||||||
|
first-launch flow; this is the exception to the routine-signing rule above.
|
||||||
|
Do not assume the platform's eager-picker app list contains a new app ID.
|
||||||
|
Consume an already selected identity through `getSelectedIdentity()` or the
|
||||||
|
sticky `onIdentitySelected()` subscription to avoid a second chooser.
|
||||||
|
Preserve manual login/account switching, external extensions and remote
|
||||||
|
signers. Cancellation must leave a usable login screen without reopening a
|
||||||
|
prompt loop; signing still requires the platform's normal consent.
|
||||||
|
- Test the actual HTTP LAN/Tailscale address, not only localhost or HTTPS.
|
||||||
|
`crypto.subtle` and clipboard APIs may be unavailable on those addresses.
|
||||||
|
Keep authenticated encryption: use a vetted compatible implementation when
|
||||||
|
WebCrypto is absent, and secure randomness (`crypto.getRandomValues`). Test
|
||||||
|
existing-message decryption, tamper rejection and an HTTP round trip.
|
||||||
|
- Verify first launch, cancellation/retry, reload, owner/viewer authorization,
|
||||||
|
and persisted data after app recreation. Use the shared browser-check suite
|
||||||
|
outside the repository; record which nodes and browser engines were tested.
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# Archipelago Developer Guide
|
# Archipelago Developer Guide
|
||||||
|
|
||||||
|
For new apps, start with `docs/app-developer-guide.md` and complete its **Launch acceptance: credentials, signer, and HTTP nodes** checklist. Packaging includes My Apps presentation, login/first-run credential handoff, native signer startup, and real HTTP-node testing—not only a working container.
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -143,6 +143,22 @@ describe('useAppLauncherStore', () => {
|
|||||||
expect(mockWindowOpen).not.toHaveBeenCalled()
|
expect(mockWindowOpen).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('shows DATUM generated credentials before opening its embedded UI', async () => {
|
||||||
|
mockRpcCall.mockResolvedValueOnce({
|
||||||
|
title: 'DATUM Gateway login',
|
||||||
|
credentials: [{ label: 'Password', value: 'fixture-only-password', sensitive: true }],
|
||||||
|
})
|
||||||
|
const store = useAppLauncherStore()
|
||||||
|
store.openSession('datum')
|
||||||
|
await vi.waitFor(() => expect(store.credentialPrompt.loading).toBe(false))
|
||||||
|
expect(store.credentialPrompt.show).toBe(true)
|
||||||
|
expect(store.credentialPrompt.credentials[0]?.value).toBe('fixture-only-password')
|
||||||
|
expect(store.panelAppId).toBeNull()
|
||||||
|
expect(mockWindowOpen).not.toHaveBeenCalled()
|
||||||
|
store.continueCredentialLaunch()
|
||||||
|
expect(store.panelAppId).toBe('datum')
|
||||||
|
})
|
||||||
|
|
||||||
it('gates a Home-style Portainer launch until its first-run token is shown', async () => {
|
it('gates a Home-style Portainer launch until its first-run token is shown', async () => {
|
||||||
mockRpcCall.mockResolvedValueOnce({
|
mockRpcCall.mockResolvedValueOnce({
|
||||||
title: 'Portainer first-run token',
|
title: 'Portainer first-run token',
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ const NEW_TAB_APP_IDS = new Set([
|
|||||||
* original synchronous user gesture. Portainer is dynamic (first-run only);
|
* original synchronous user gesture. Portainer is dynamic (first-run only);
|
||||||
* File Browser and PhotoPrism have stable fallback credentials. */
|
* File Browser and PhotoPrism have stable fallback credentials. */
|
||||||
export const CREDENTIAL_INTERSTITIAL_APPS = new Set([
|
export const CREDENTIAL_INTERSTITIAL_APPS = new Set([
|
||||||
|
'datum',
|
||||||
'filebrowser',
|
'filebrowser',
|
||||||
'photoprism',
|
'photoprism',
|
||||||
'portainer',
|
'portainer',
|
||||||
|
|||||||
Reference in New Issue
Block a user