{"id":44840786,"url":"https://github.com/d-buckner/bloud","last_synced_at":"2026-05-17T05:05:22.320Z","repository":{"id":332042444,"uuid":"1132500590","full_name":"d-buckner/bloud","owner":"d-buckner","description":"your home cloud made easier","archived":false,"fork":false,"pushed_at":"2026-03-08T21:50:31.000Z","size":1776,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-09T02:41:19.331Z","etag":null,"topics":["cloud","cloud-os","go","nixos","operating-system","self-hosted","self-hosting","selfhosted","svelte"],"latest_commit_sha":null,"homepage":"https://bloud.co","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/d-buckner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"docs/roadmap.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-12T03:35:57.000Z","updated_at":"2026-03-08T21:43:22.000Z","dependencies_parsed_at":"2026-02-06T07:05:44.954Z","dependency_job_id":null,"html_url":"https://github.com/d-buckner/bloud","commit_stats":null,"previous_names":["d-buckner/bloud"],"tags_count":58,"template":false,"template_full_name":null,"purl":"pkg:github/d-buckner/bloud","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-buckner%2Fbloud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-buckner%2Fbloud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-buckner%2Fbloud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-buckner%2Fbloud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/d-buckner","download_url":"https://codeload.github.com/d-buckner/bloud/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-buckner%2Fbloud/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30416310,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T04:41:02.746Z","status":"ssl_error","status_checked_at":"2026-03-12T04:40:12.571Z","response_time":114,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloud","cloud-os","go","nixos","operating-system","self-hosted","self-hosting","selfhosted","svelte"],"created_at":"2026-02-17T03:15:02.449Z","updated_at":"2026-05-17T05:05:22.312Z","avatar_url":"https://github.com/d-buckner.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bloud\n\n**Home Cloud Operating System**\n\n[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](LICENSE)\n[![Status: Alpha](https://img.shields.io/badge/Status-Alpha-orange.svg)]()\n\n\u003e **Status:** Early alpha. Core infrastructure and web UI working.\n\n---\n\nSelf-hosting's hard part isn't installation. Every platform has solved that. The hard part is  when you add a second service and realize the first one doesn't know it exists.\n\nTo connect two services manually, you typically:\n\n- Generate an API key in one service\n- Open the second service's settings, paste the key\n- Create an OAuth client in your identity provider - client ID, secret, callback URL\n- Paste those back into the first service\n- Register the second service with the identity provider separately\n- Write a reverse proxy rule for each\n- Provision and wire a database for each\n- Remember all of this when you reinstall or migrate\n\nOn every other platform, *you* are the integration layer — the person who knows which credentials go where and how the pieces fit together. You don't configure services. You configure the relationships between services. That knowledge lives in your head, not in the platform.\n\n**Bloud flips this.** Apps declare what they provide and what they consume. The system holds the integration knowledge and wires everything automatically - on install, on restart, forever.\n\n```mermaid\ngraph LR\n    DB[(Database)]\n    IP[Identity Provider]\n    S1[Service]\n    S2[Service]\n    RP[Reverse Proxy]\n\n    DB --\u003e|credentials| S1\n    DB --\u003e|credentials| S2\n    DB --\u003e|credentials| IP\n    S1 --\u003e|SSO client| IP\n    S2 --\u003e|SSO client| IP\n    S1 \u003c--\u003e|API key| S2\n    S1 --\u003e|route| RP\n    S2 --\u003e|route| RP\n    IP --\u003e|auth middleware| RP\n```\n\nEvery edge in this graph is a manual step on every other platform. Bloud generates all of them and regenerates them correctly every time a service starts.\n\n---\n\n## The Vision\n\n- Flash USB drive, boot on any x86_64 hardware\n- Access web UI, install apps with one click\n- Every integration automatic: SSO, routing, credentials, app-to-app connections\n- Multi-host orchestration for scaling across machines\n\n---\n\n## Quick Start\n\nBloud runs as a bootable ISO. Flash it to a USB drive or deploy it to a VM.\n\nFor **development**, you'll need a NixOS machine (see [Local Development](#local-development) below).\n\n```bash\ngit clone https://codeberg.org/d-buckner/bloud.git\ncd bloud\nnpm run setup    # Install deps, build CLI\n./bloud setup    # Check prerequisites, apply NixOS config\n./bloud start    # Start dev environment\n```\n\nAccess the web UI at **http://bloud.local** (port 80, through Traefik).\n\n---\n\n## Apps\n\n| Category           | Apps                                  |\n| ------------------ | ------------------------------------- |\n| **Infrastructure** | PostgreSQL, Redis, Traefik, Authentik |\n| **Media**          | Jellyfin                              |\n| **Productivity**   | Miniflux (RSS)                        |\n| **Network**        | AdGuard Home                          |\n| **Utility**        | qBittorrent                           |\n\n---\n\n## How It Works\n\n### 1. The Dependency Graph\n\nEvery app declares its integrations in `metadata.yaml`:\n\n```yaml\n# example metadata.yaml\nintegrations:\n  database:\n    required: true\n    compatible:\n      - app: postgres\n        default: true\n  sso:\n    required: false\n    compatible:\n      - app: authentik\n```\n\nWhen you install an app, Bloud builds a graph of everything it needs. Dependencies with only one compatible option are auto-selected. If postgres isn't installed yet, it goes in first. If it's already there, the new app just gets wired to it.\n\nThe graph also determines **execution order** — infrastructure first, then dependents:\n\n```\nLevel 0: postgres, redis          ← No dependencies\nLevel 1: authentik                ← Needs postgres + redis\nLevel 2: jellyfin, miniflux       ← Need postgres and authentik\n```\n\n### 2. Declarative Container Definitions\n\nEach app has a `module.nix` defining how to run the container:\n\n```nix\nmkBloudApp {\n  name = \"miniflux\";\n  image = \"miniflux/miniflux:latest\";\n  port = 8085;\n  database = \"miniflux\";  # Auto-creates postgres DB and user\n\n  environment = cfg: {\n    BASE_URL = \"${cfg.externalHost}/embed/miniflux\";\n  };\n}\n```\n\nThe `mkBloudApp` helper handles systemd services, podman networking, volumes, and dependency wiring. When you enable an app, NixOS creates everything atomically.\n\n### 3. Idempotent Configurators\n\nSSO, routing, and credentials are wired automatically by the platform — apps don't configure those themselves. What configurators handle is app-specific setup that can't be expressed in Nix: creating directories, writing config files, setting app-specific defaults.\n\n```go\n// PreStart: runs before the container starts\nfunc (c *Configurator) PreStart(ctx context.Context, state *AppState) error {\n    // Ensure directories exist\n    if err := os.MkdirAll(filepath.Join(state.DataPath, \"config\"), 0755); err != nil {\n        return err\n    }\n\n    // Write app config with required settings for Bloud's embedding\n    ini, _ := configurator.LoadINI(configPath)\n    ini.EnsureKeys(\"Preferences\", map[string]string{\n        \"WebUI\\\\HostHeaderValidation\": \"false\",\n        \"WebUI\\\\CSRFProtection\":       \"false\",\n        \"Downloads\\\\SavePath\":         \"/downloads\",\n    })\n    return ini.Save(configPath)\n}\n\n// PostStart: runs after the container is healthy\n// Used for apps that require API calls to configure (e.g. Authentik)\nfunc (c *Configurator) PostStart(ctx context.Context, state *AppState) error {\n    return nil // most apps are fully configured by PreStart\n}\n```\n\nConfigurators always write the *desired* state — running them again produces the same result.\n\n### 4. Container Invalidation\n\nWhen a new integration becomes available, existing apps that can use it get automatically reconfigured:\n\n```\nService A installed (no SSO)\nIdentity provider installed\n    │\n    ▼\nGraph: \"Who declared an SSO integration?\"\n    → Service A\n    │\n    ▼\nService A marked for reconfiguration\nService A restarted in dependency order\nPreStart + PostStart wire the new integration\n```\n\nNo user action needed. Apps gain integrations as you install their dependencies.\n\n---\n\n## Shared Infrastructure\n\nEach Bloud host runs exactly one instance of each infrastructure service. All apps share them:\n\n- **PostgreSQL** — one instance, apps get separate databases and users\n- **Redis** — session storage and caching, shared across apps\n- **Traefik** — reverse proxy, routing, SSO middleware\n- **Authentik** — identity provider, SSO for all apps\n\n---\n\n## SSO Integration\n\nApps get SSO automatically. Three strategies depending on the app:\n\n| Strategy         | How It Works                                                          | Example Apps              |\n| ---------------- | --------------------------------------------------------------------- | ------------------------- |\n| **Native OIDC**  | App handles OAuth2 itself; Bloud provides credentials via env vars    | Miniflux                  |\n| **Forward Auth** | Traefik checks auth with Authentik before the request reaches the app | AdGuard Home, qBittorrent |\n| **LDAP**         | Authentik LDAP for apps that don't speak OAuth2                       | Jellyfin                  |\n\nAll three are configured automatically at install time.\n\n---\n\n## Project Structure\n\n```\nbloud/\n├── apps/                          # App definitions\n│   ├── miniflux/\n│   │   ├── metadata.yaml          # Integrations, SSO, port, routing\n│   │   ├── module.nix             # Container definition\n│   │   └── configurator.go        # PreStart/PostStart hooks\n│   ├── postgres/\n│   ├── authentik/\n│   └── ...\n│\n├── services/host-agent/           # Go backend\n│   ├── cmd/host-agent/\n│   ├── internal/\n│   │   ├── orchestrator/          # Install/uninstall coordination\n│   │   ├── catalog/               # App graph and dependency resolution\n│   │   ├── nixgen/                # Generates apps.nix, Traefik config, blueprints\n│   │   ├── store/                 # Database layer\n│   │   └── api/                   # HTTP API\n│   ├── pkg/configurator/          # Configurator interface\n│   └── web/                       # Svelte frontend\n│\n├── nixos/\n│   ├── bloud.nix                  # Main NixOS module\n│   ├── lib/\n│   │   ├── bloud-app.nix          # mkBloudApp helper\n│   │   └── podman-service.nix     # Systemd service generator\n│   └── generated/\n│       └── apps.nix               # Generated by host-agent on install\n│\n└── cli/                           # ./bloud CLI (Go)\n```\n\n---\n\n## Local Development\n\nRequires a NixOS machine (physical or VM with the `dev-server` config applied).\n\n```bash\nnpm run setup    # Install deps + build ./bloud CLI\n./bloud setup    # Check prerequisites, apply NixOS config\n./bloud start    # Start dev environment\n```\n\n### CLI Commands\n\n**Native NixOS mode** (development on a NixOS machine):\n\n```bash\n./bloud start          # Start dev environment\n./bloud stop           # Stop dev services\n./bloud status         # Show status\n./bloud logs           # Show logs\n./bloud attach         # Attach to tmux session (Ctrl-B D to detach)\n./bloud shell [cmd]    # Run a command or open a shell\n./bloud rebuild        # Apply NixOS config changes\n```\n\n**Proxmox mode** (ISO integration testing, set `BLOUD_PVE_HOST`):\n\n```bash\n./bloud start [iso]          # Deploy ISO → create VM → boot → check\n./bloud start --skip-deploy  # Reuse existing VM, re-run checks\n./bloud stop                 # Stop VM\n./bloud destroy              # Destroy VM\n./bloud shell [cmd]          # SSH into VM\n./bloud checks               # Run health checks\n./bloud install \u003capp\u003e        # Install app via API\n```\n\n### After Changing NixOS Config\n\n```bash\n./bloud rebuild   # Apply changes to the running dev machine\n```\n\n### Debugging\n\n```bash\n# Check host-agent logs\njournalctl --user -u bloud-host-agent -f\n\n# Check app container logs (includes PreStart/PostStart output)\njournalctl --user -u podman-miniflux -f\n\n# Restart a service to re-run configurators\nsystemctl --user restart podman-miniflux\n\n# Check install plan (shows resolved dependency graph)\ncurl http://localhost/api/apps/miniflux/plan-install\n```\n\n### Contributing a New App\n\n1. Create `apps/myapp/metadata.yaml` — integrations, SSO strategy, port\n2. Create `apps/myapp/module.nix` — container definition using `mkBloudApp`\n3. Create `apps/myapp/configurator.go` — PreStart, HealthCheck, PostStart\n4. Register in `services/host-agent/internal/appconfig/register.go`\n\nSee [docs/contributing-apps.md](docs/contributing-apps.md) for details.\n\n---\n\n## Further Reading\n\n- [docs/embedded-app-routing.md](docs/embedded-app-routing.md) — How apps are served in iframes\n- [docs/design/authentication.md](docs/design/authentication.md) — SSO and auth flows\n- [apps/adding-apps.md](apps/adding-apps.md) — Contributor guide for new apps\n\n---\n\n## Contributing\n\nContributions welcome.\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Open a Pull Request\n\n[Open an issue](https://codeberg.org/d-buckner/bloud/issues) with a clear description and steps to reproduce for bugs.\n\n---\n\n## License\n\nAGPL v3 — See [LICENSE](LICENSE) for details. If you modify Bloud and offer it as a service, you must share your changes.\n\n---\n\n**Built with NixOS, Podman, Systemd, Go, and Svelte.**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd-buckner%2Fbloud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fd-buckner%2Fbloud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd-buckner%2Fbloud/lists"}