RepoPilotOpen in app →

glanceapp/glance

A self-hosted dashboard that puts all your feeds in one place

Mixed

Slowing — last commit 5mo ago

worst of 4 axes
Use as dependencyConcerns

copyleft license (AGPL-3.0) — review compatibility; no tests detected

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 5mo ago
  • 12 active contributors
  • AGPL-3.0 licensed
Show 5 more →
  • CI configured
  • Slowing — last commit 5mo ago
  • Single-maintainer risk — top contributor 88% of recent commits
  • AGPL-3.0 is copyleft — check downstream compatibility
  • No test directory detected
What would change the summary?
  • Use as dependency ConcernsMixed if: relicense under MIT/Apache-2.0 (rare for established libs)

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/glanceapp/glance?axis=fork)](https://repopilot.app/r/glanceapp/glance)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/glanceapp/glance on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: glanceapp/glance

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/glanceapp/glance shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Slowing — last commit 5mo ago

  • Last commit 5mo ago
  • 12 active contributors
  • AGPL-3.0 licensed
  • CI configured
  • ⚠ Slowing — last commit 5mo ago
  • ⚠ Single-maintainer risk — top contributor 88% of recent commits
  • ⚠ AGPL-3.0 is copyleft — check downstream compatibility
  • ⚠ No test directory detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live glanceapp/glance repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/glanceapp/glance.

What it runs against: a local clone of glanceapp/glance — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in glanceapp/glance | Confirms the artifact applies here, not a fork | | 2 | License is still AGPL-3.0 | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 180 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>glanceapp/glance</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of glanceapp/glance. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/glanceapp/glance.git
#   cd glance
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of glanceapp/glance and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "glanceapp/glance(\\.git)?\\b" \\
  && ok "origin remote is glanceapp/glance" \\
  || miss "origin remote is not glanceapp/glance (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(AGPL-3\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"AGPL-3\\.0\"" package.json 2>/dev/null) \\
  && ok "license is AGPL-3.0" \\
  || miss "license drift — was AGPL-3.0 at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "internal/glance/main.go" \\
  && ok "internal/glance/main.go" \\
  || miss "missing critical file: internal/glance/main.go"
test -f "internal/glance/config.go" \\
  && ok "internal/glance/config.go" \\
  || miss "missing critical file: internal/glance/config.go"
test -f "internal/glance/glance.go" \\
  && ok "internal/glance/glance.go" \\
  || miss "missing critical file: internal/glance/glance.go"
test -f "internal/glance/auth.go" \\
  && ok "internal/glance/auth.go" \\
  || miss "missing critical file: internal/glance/auth.go"
test -f "go.mod" \\
  && ok "go.mod" \\
  || miss "missing critical file: go.mod"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 180 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~150d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/glanceapp/glance"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Glance is a lightweight, self-hosted dashboard application written in Go that aggregates multiple feed sources (RSS, Reddit, Hacker News, YouTube, Twitch, weather, market data, Docker stats, server metrics) into a single customizable web interface. It compiles to a <20MB binary with minimal dependencies and serves pages that typically load in ~1s, making it ideal for personal dashboards that run on low-resource hardware. Monolithic Go binary structure: main application code likely in root with templates/ for HTML, static/ for CSS/JS, and docs/ for configuration markdown. Widget system appears plugin-style with declarative YAML configuration parsed in main binary. Frontend is vanilla JavaScript with minimal dependencies (no framework overhead), communicating with Go backend via HTTP.

👥Who it's for

Self-hosted enthusiasts and homelab operators who want a centralized dashboard for monitoring feeds and services without relying on cloud platforms. Also appeals to developers who prefer single-binary deployments and minimal JavaScript footprints over heavyweight SPA frameworks.

🌱Maturity & risk

Actively maintained and production-ready: the codebase is substantial (256KB Go, 75KB HTML, 69KB JavaScript), includes Docker support with goreleaser automation, has structured issue templates and pull request workflows, and maintains comprehensive documentation in docs/. The Go version 1.24.3 requirement and clean dependency management suggest ongoing development.

Low-risk profile: only 9 direct Go dependencies (fsnotify, gofeed, gopsutil, gjson, crypto, yaml, etc.) with well-maintained upstream projects, small attack surface due to single-binary architecture, and no evidence of single-maintainer bottleneck (GitHub workflows and community widgets repo suggest distributed governance). Main risk is feature creep as the widget ecosystem grows—careful API versioning will be needed.

Active areas of work

Active development with recent Go 1.24.3 adoption, goreleaser-based release automation (.goreleaser.yaml), and an established ecosystem (community-widgets repo, preconfigured pages, themes documentation). Release workflow in .github/workflows/release.yaml indicates regular binaries published for multiple OS/architecture combinations.

🚀Get running

git clone https://github.com/glanceapp/glance.git
cd glance
go build -o glance .
./glance

The application will start the HTTP server (default port typically 8080; check README or docs/configuration.md for port configuration). Configuration is via YAML file (glance.yml or environment variable).

Daily commands: After go build -o glance ., run ./glance (expects glance.yml in working directory or set via env var). For development, modify Go source files and rebuild. Frontend changes (HTML/CSS/JS) are in static/ and reloaded on restart. Use Dockerfile for containerized runs: docker build -t glance . && docker run -p 8080:8080 glance.

🗺️Map of the codebase

  • internal/glance/main.go — Application entry point that initializes the server, loads configuration, and starts the HTTP listener; every contributor must understand the startup flow.
  • internal/glance/config.go — Core configuration parser that unmarshals YAML and defines the data structures for pages, columns, and widgets; fundamental to understanding how Glance is customized.
  • internal/glance/glance.go — Main application struct and request handlers that coordinate page rendering and data fetching; the central orchestrator of the dashboard logic.
  • internal/glance/auth.go — Authentication and access control middleware for protecting sensitive dashboard routes and verifying user tokens.
  • go.mod — Go module dependencies file defining all external libraries (gofeed, gopsutil, yaml) that power widget functionality.
  • docs/configuration.md — Comprehensive configuration guide documenting all available widgets, options, and YAML structure; essential for understanding what can be configured.
  • Dockerfile — Container image definition showing the runtime environment, dependencies, and deployment configuration for self-hosted dashboards.

🧩Components & responsibilities

  • Config Parser (config.go, config-fields.go) (gopkg.in/yaml.v3) — Reads YAML file, unmarshals into Go structs, validates required fields and options
    • Failure mode: Invalid YAML syntax or unknown widget types; returns parse error and application fails to start
  • HTTP Server (main.go, glance.go) (Go net/http, net.Listener) — Listens for incoming requests, routes to handlers, coordinates page rendering and asset serving
    • Failure mode: Port already in use, network bind error, or request handling panic—results in server crash or unresponsive endpoints
  • Widget Fetcher (glance.go) (net/http, gofeed, gopsutil, external APIs) — Executes data fetching logic for each widget type (RSS, weather, stats, etc.) and aggregates results
    • Failure mode: Timeout, API rate limit, or network error causes widget to fail; page render delays or missing data
  • Auth Middleware (auth.go) (crypto/sha256, token validation) — Validates authentication tokens on protected routes before allowing access
    • Failure mode: Invalid token or missing auth header; request rejected with

🛠️How to make changes

Add a new widget type

  1. Define the widget struct and configuration fields in the Widget-related sections of config.go (internal/glance/config.go)
  2. Implement data fetching logic as a method on the widget struct (e.g., FetchRSSFeed, FetchWeather) using Go's net/http package and external APIs defined in go.mod (internal/glance/glance.go)
  3. Add a new HTML template or update the embedded frontend to render the widget's data structure (internal/glance/embed.go)
  4. Document the new widget's configuration options, required fields, and examples in the configuration guide (docs/configuration.md)

Add authentication to a new endpoint

  1. Define the new HTTP route handler in the main application struct (internal/glance/glance.go)
  2. Wrap the handler with the auth middleware to check tokens or session tokens (internal/glance/auth.go)
  3. Document the authentication requirements and token generation process in the README or configuration guide (docs/configuration.md)

Add a new configuration option

  1. Add the new field to the relevant struct (Page, Column, Widget) with YAML tags and metadata (internal/glance/config-fields.go)
  2. Update the YAML unmarshaling logic to validate and apply the new option during config parsing (internal/glance/config.go)
  3. Update your widget or application handler to use the new configuration option (internal/glance/glance.go)
  4. Document the option in the configuration guide with examples and valid values (docs/configuration.md)

🔧Why these technologies

  • Go — Lightweight, fast, single-binary deployment; ideal for a self-hosted dashboard with minimal resource footprint
  • YAML (gopkg.in/yaml.v3) — Human-readable configuration format commonly used in self-hosted tools; mirrors docker-compose and Kubernetes conventions
  • Embedded static assets (embed.go) — Enables standalone binary distribution without external file dependencies; simplifies Docker deployment
  • gofeed (RSS/Atom parser) — Handles complex RSS/Atom feed parsing; avoids reinventing feed parsing logic
  • gopsutil (system metrics) — Cross-platform system stats (CPU, memory, disk) without shelling out to external commands

⚖️Trade-offs already made

  • Single Go binary with embedded assets instead of Node.js + npm

    • Why: Reduces deployment complexity and runtime dependencies; targets resource-constrained selfhosted environments
    • Consequence: Limited frontend framework capabilities; custom vanilla JS for interactivity rather than React/Vue
  • YAML configuration instead of database/UI configurator

    • Why: Aligns with DevOps practices (IaC); avoids runtime database dependency
    • Consequence: Configuration changes require file edits and restart; no in-browser config UI
  • Synchronous widget data fetching on-request rather than background workers

    • Why: Simpler architecture without job queues or async frameworks
    • Consequence: Dashboard load time depends on external API response times; no data prefetching or caching layer
  • Token-based auth instead of OAuth/SAML

    • Why: Self-hosted context does not require enterprise auth protocols; simpler implementation
    • Consequence: Manual token generation required; no standard SSO integration

🚫Non-goals (don't propose these)

  • Does not persist user data or widget state; config is source of truth
  • Does not provide a UI for configuration—all customization is YAML
  • Does not support real-time updates or WebSocket connections for live data
  • Does not implement a database layer; no long-term data storage
  • Not designed for multi-user collaboration or role-based access control
  • Does not cache external API responses across restarts

🪤Traps & gotchas

Configuration must be valid YAML and present at startup (no live reload hinted by fsnotify dependency, but unclear if config changes are watched). Feed refresh intervals are widget-specific via 'cache' parameter (e.g., '12h')—misconfiguration can hammer upstream APIs. Docker deployment expects volume mount for config file persistence. Some widgets require API keys (market data, weather) not shown in example config. No built-in authentication—assume running behind reverse proxy with auth layer if exposed to internet.

🏗️Architecture

💡Concepts to learn

  • RSS/Atom Feed Parsing — Core functionality of Glance; gofeed library abstracts the details, but understanding feed standards (RSS 2.0, Atom 1.0, JSON Feed) helps troubleshoot widget failures and extend support
  • YAML Configuration Schema — All Glance configuration is declarative YAML (pages, columns, widgets, themes); understanding the unmarshaling pattern in Go (gopkg.in/yaml.v3) is essential for adding new widgets or configuration options
  • Cross-Platform System Metrics Collection — gopsutil/v4 enables the server-stats and docker-containers widgets to gather CPU, memory, and process data portably; understanding syscall abstraction helps debug metrics discrepancies
  • File Watch Patterns (fsnotify) — fsnotify dependency suggests hot-reloading or monitoring of configuration files; understanding inotify (Linux) vs kqueue (macOS) is useful for debugging file watch issues in containerized deployments
  • Widget Component Model (Declarative over Imperative) — Glance uses a page > column > widget hierarchy defined in YAML rather than code; this design choice enables non-developers to create layouts without touching Go, fundamental to its appeal
  • Single Binary Deployment Pattern — Go's ability to compile a static binary with embedded assets (templates, static files) with zero external dependencies is a core strength; understanding how static files are served (likely http.FileServer or embed package) is key to the architecture
  • Caching Strategies for External APIs — Each widget's 'cache' parameter (e.g., '12h' for RSS) controls refresh intervals; understanding TTL-based caching and rate limiting is critical to avoid hammering upstream services like Reddit, YouTube, and weather APIs
  • MerlinScheurer/Organizr — Similar self-hosted dashboard that aggregates services and feeds; competitive alternative in the same niche
  • causefx/Organizr — Another popular self-hosted dashboard aggregator with iframe-based widget model; different architecture approach to the same problem
  • glanceapp/community-widgets — Official companion repo for third-party widget extensions; shows how the ecosystem is meant to be extended
  • gophish/gophish — Demonstrates Go single-binary architecture with minimal dependencies similar to Glance's deployment model
  • mmcdole/gofeed — Direct upstream dependency used for RSS/Atom parsing; understanding its API is crucial for RSS widget development

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for widget parsing and rendering

The repo has multiple widget types (RSS, Reddit, Hacker News, Docker, etc.) but no visible test files in the structure. Widget parsing and data transformation are critical paths that would benefit from unit tests to prevent regressions when adding new widgets or modifying existing ones.

  • [ ] Create tests directory mirroring the source structure
  • [ ] Add tests for gofeed parsing (RSS widget) with edge cases like malformed feeds
  • [ ] Add tests for JSON parsing logic in tidwall/gjson integrations (used across multiple widgets)
  • [ ] Add tests for YAML configuration parsing (gopkg.in/yaml.v3) to validate config marshaling
  • [ ] Test widget data transformation pipelines (e.g., market prices, server stats calculations)

Document custom-api.md with complete working examples for each data source type

docs/custom-api.md exists but based on the feature list, there are many data source types (Reddit, YouTube, Twitch, Docker, etc.). The custom API documentation should include runnable examples showing how to fetch and transform data from each type, reducing friction for contributors building extensions.

  • [ ] Audit docs/custom-api.md against all widget types mentioned in configuration.md
  • [ ] Add a working example for REST API responses with tidwall/gjson query syntax
  • [ ] Add a working example for HTML scraping patterns (using PuerkitoBio/goquery from dependencies)
  • [ ] Add a working example for XML/RSS parsing using mmcdole/gofeed patterns
  • [ ] Add a working example showing how to handle paginated APIs with the custom widget framework

Add integration test workflow for Docker builds with multiple Go versions

The go.mod specifies go 1.24.3, and .goreleaser.yaml and Dockerfile indicate multi-platform releases. However, .github/workflows only shows release.yaml. Adding a workflow that tests Docker builds across supported Go versions and architectures would catch breaking changes early and ensure consistent behavior across platforms.

  • [ ] Create .github/workflows/docker-test.yaml (triggered on PR to Dockerfile, go.mod, main source changes)
  • [ ] Test Dockerfile build against go 1.23.x and go 1.24.x to ensure backwards compatibility
  • [ ] Add matrix job testing builds for linux/amd64, linux/arm64, and linux/arm/v7
  • [ ] Add smoke test that runs the container and verifies HTTP endpoint responds with status 200
  • [ ] Verify Dockerfile.goreleaser builds correctly for release scenarios

🌿Good first issues

  • Add unit tests for widget configuration parsing: the YAML schema has many widgets (RSS, Reddit, Hacker News, weather, etc.) but test coverage is unclear; start with go test in root and add tests/widget_config_test.go for parsing edge cases
  • Improve mobile CSS for small screens: docs/images/mobile-preview.png exists but mobile responsiveness may have gaps; audit static/mobile.css and add media queries for widget card layouts on screens <375px wide
  • Document API key setup for weather/market widgets: docs/configuration.md shows widget config but doesn't explain where to obtain API keys or how to set them as env vars; add a section with provider-specific links (OpenWeatherMap, Alpha Vantage, etc.)

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 6c5b7a3 — Update docs (svilenmarkov)
  • 36d5ae0 — Merge pull request #848 from fullmetalsheep/main (svilenmarkov)
  • 478c08f — feat(themes): Add theme 'Neon Pink' (fullmetalsheep)
  • cae90d1 — Update theme preview (svilenmarkov)
  • fbc07bd — Merge pull request #833 from nicolasluckie/feat/add-shades-of-purple-theme (svilenmarkov)
  • 4a4d3e1 — Add contrast-multiplier to shades of purple (svilenmarkov)
  • f243a49 — Update readme (svilenmarkov)
  • 9416de1 — Fix indentation (svilenmarkov)
  • 283a5fc — feat: add Shades of Purple theme with screenshot (nicolasluckie)
  • c88fd52 — Merge branch 'dev' (svilenmarkov)

🔒Security observations

The Glance application demonstrates generally good security practices with a statically compiled Go binary, minimal dependencies, and use of Alpine Linux. However, there are areas for improvement: (1) Security headers should be explicitly configured to prevent XSS and clickjacking attacks, (2) External feed fetching should implement URL validation and SSRF protections, (3) Docker configuration should enforce non-root user execution and resource limits, (4) Dependencies should be regularly updated and scanned for vulnerabilities. The application's ability to fetch content from arbitrary external sources (RSS, APIs, social media) requires robust input validation and sanitization to prevent injection attacks.

  • Medium · Outdated Go Version in Docker — Dockerfile (line 1). The Dockerfile uses Go 1.24.3, which may contain known vulnerabilities. The project should regularly update to the latest stable Go version to receive security patches. Fix: Regularly update Go version to the latest stable release. Monitor security advisories at https://golang.org/security
  • Medium · Outdated Alpine Base Image — Dockerfile (line 7). Alpine 3.21 is used as the base image. While relatively recent, it should be kept updated to patch known vulnerabilities in system libraries. Fix: Implement automated dependency scanning and update Alpine Linux regularly. Use tools like Dependabot or Trivy for vulnerability scanning.
  • Medium · Missing Security Headers Configuration — Dockerfile (line 11), Application configuration. The application exposes port 8080 without explicit security header configuration visible in the provided code. This could allow XSS, clickjacking, or other header-based attacks. Fix: Implement security headers (Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security) in the HTTP server configuration.
  • Low · No Resource Limits in Docker Configuration — Dockerfile. The Dockerfile does not specify memory or CPU limits, which could lead to resource exhaustion attacks if the application is exposed to untrusted networks. Fix: Implement resource limits in docker-compose.yml or orchestration platform (e.g., memory: 256M, cpus: '0.5')
  • Low · Non-Root User Not Enforced in Docker — Dockerfile. The Dockerfile does not explicitly create and use a non-root user for running the application, increasing the impact of potential container escapes. Fix: Add a USER directive to run the application as a non-privileged user (e.g., 'USER nobody' or create a dedicated appuser)
  • Low · Potential External Feed Injection Risk — Application logic (inferred from widgets: RSS, YouTube, Reddit, Twitch, custom API). The application fetches RSS feeds, YouTube channels, Reddit posts, and data from external sources without visible validation of content. This could expose users to malicious content or SSRF attacks if URLs are not properly validated. Fix: Implement URL validation and whitelist protocols (http/https only). Use a timeout for external requests. Sanitize content from external feeds before rendering.
  • Low · Custom API Widget Security Concerns — docs/custom-api.md, Application logic. The application supports custom API widgets that fetch arbitrary data. Without strict validation, this could enable SSRF attacks, credential exposure, or data exfiltration. Fix: Implement strict URL validation, disable access to internal IPs (127.0.0.1, 10.0.0.0/8, etc.), enforce timeouts, and validate response content types.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · glanceapp/glance — RepoPilot