# Archipelago Developer Guide ## Project Structure ``` archy/ ├── core/ # Rust backend │ └── archipelago/ │ ├── src/ │ │ ├── main.rs # Entry point, module declarations │ │ ├── api/rpc/ # RPC endpoint handlers │ │ │ ├── mod.rs # Route dispatcher │ │ │ ├── auth.rs # Login, session, TOTP │ │ │ ├── container.rs # Container lifecycle │ │ │ ├── package.rs # Package install/remove │ │ │ ├── interfaces.rs # Network interfaces, WiFi, DNS │ │ │ ├── federation.rs # Federation management │ │ │ ├── marketplace.rs # Community marketplace │ │ │ └── ... # Other endpoint groups │ │ ├── auth.rs # Password hashing, sessions │ │ ├── config.rs # Configuration loading │ │ ├── server.rs # HTTP/WS server (axum) │ │ ├── container/ # Podman integration │ │ ├── network/ # Network management │ │ │ ├── dns.rs # DNS configuration │ │ │ ├── router.rs # UPnP, diagnostics │ │ │ └── dwn_*.rs # DWN protocol │ │ ├── federation.rs # Federation protocol │ │ ├── marketplace.rs # Marketplace discovery │ │ ├── identity.rs # DID key management │ │ ├── vpn.rs # VPN (Tailscale/WireGuard) │ │ ├── mesh.rs # Meshtastic mesh networking │ │ └── ... │ ├── Cargo.toml │ └── tests/ # Integration tests ├── neode-ui/ # Vue 3 frontend │ ├── src/ │ │ ├── api/ # RPC client, WebSocket, container client │ │ │ └── rpc-client.ts # Central RPC client (all backend calls) │ │ ├── views/ # Page components │ │ │ ├── Home.vue # Dashboard with system stats │ │ │ ├── Marketplace.vue # App store (curated + community) │ │ │ ├── Server.vue # Network, VPN, DNS management │ │ │ ├── Federation.vue # Federation dashboard │ │ │ ├── Settings.vue # User settings │ │ │ ├── Web5.vue # DID, DWN, Nostr │ │ │ └── ... │ │ ├── stores/ # Pinia state management │ │ ├── components/ # Reusable UI components │ │ ├── composables/ # Vue composables │ │ ├── router/ # Vue Router with guards │ │ ├── types/ # TypeScript type definitions │ │ └── style.css # Global styles + Tailwind utilities │ ├── vite.config.ts │ └── package.json ├── scripts/ # Deployment and utility scripts │ ├── deploy-to-target.sh # Main deploy script │ ├── first-boot-containers.sh # ISO first-boot setup │ └── run-tests.sh # CI test runner ├── image-recipe/ # ISO build configuration │ ├── build-auto-installer-iso.sh │ └── configs/ # Nginx, systemd configs ├── docs/ # Documentation │ ├── architecture.md │ ├── app-manifest-spec.md │ ├── marketplace-protocol.md │ └── multi-node-architecture.md ├── apps/ # App manifests (YAML) ├── CLAUDE.md # AI development instructions └── loop/plan.md # Project roadmap ``` ## Development Setup ### Prerequisites - **macOS** (development machine): Node.js 20+, npm - **Linux server** (`192.168.1.228`): Rust toolchain, Podman, Nginx, Debian 12 - SSH key: `~/.ssh/archipelago-deploy` ### Local Frontend Development ```bash cd neode-ui npm install npm start # Vite dev server on :8100, mock backend on :5959 ``` The dev server at `http://localhost:8100` uses a mock backend. Login with `password123`. ### Deploying Changes **Never build Rust on macOS.** The deploy script rsyncs source to the Linux server and builds there. ```bash # Deploy to live server (builds backend + frontend, restarts services) ./scripts/deploy-to-target.sh --live # Deploy to both servers ./scripts/deploy-to-target.sh --both ``` The deploy script: 1. Rsyncs source to the server 2. Builds Rust backend on the server (`cargo build --release`) 3. Builds Vue frontend (`npm run build`) 4. Copies artifacts to production paths 5. Restarts the `archipelago` systemd service 6. Runs a health check ### Running Tests ```bash # Frontend tests cd neode-ui && npm test # Backend tests (on dev server via SSH) ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \ "cd ~/archy/core && cargo test --all-features" # Both ./scripts/run-tests.sh ``` ## Adding a New RPC Endpoint ### 1. Create the Handler Add a handler method in the appropriate file under `core/archipelago/src/api/rpc/`. If no existing file fits, create a new one. ```rust // core/archipelago/src/api/rpc/mymodule.rs use super::RpcHandler; use anyhow::Result; impl RpcHandler { /// mymodule.action — description of what it does. pub(super) async fn handle_mymodule_action( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let name = params .get("name") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing required parameter: name"))?; // Your logic here let result = do_something(name).await?; Ok(serde_json::json!({ "ok": true, "result": result })) } } ``` **Key patterns:** - Handlers are `pub(super)` — visible only to the RPC router - Accept `Option` for params (omit for parameterless endpoints) - Return `Result` - Use `self.config.data_dir` for data persistence - Use `anyhow::bail!()` for error responses ### 2. Register the Route Add the module declaration and route in `core/archipelago/src/api/rpc/mod.rs`: ```rust // At the top: mod mymodule; // In the match statement (handle_rpc_call): "mymodule.action" => self.handle_mymodule_action(params).await, ``` ### 3. Add Module (if new) If your logic warrants a separate module: ```rust // core/archipelago/src/main.rs mod mymodule; // Add to module declarations ``` ### 4. Frontend Client Add a convenience method to `neode-ui/src/api/rpc-client.ts`: ```typescript async myAction(params: { name: string }): Promise<{ ok: boolean; result: string }> { return this.call({ method: 'mymodule.action', params, }) } ``` ### 5. Deploy and Test ```bash ./scripts/deploy-to-target.sh --live curl -X POST http://192.168.1.228/rpc/v1 \ -H "Content-Type: application/json" \ -b "archipelago_session=YOUR_SESSION" \ -d '{"method":"mymodule.action","params":{"name":"test"}}' ``` ## Adding a New Vue Page ### 1. Create the Component ```vue ``` ### 2. Add the Route In `neode-ui/src/router/index.ts`, add inside the dashboard children: ```typescript { path: 'my-page', name: 'my-page', component: () => import('@/views/MyPage.vue'), }, ``` ### 3. Standards - Always use `