GitCandy is a lightweight self-hosted Git service built on ASP.NET Core 10 MVC and EF Core. One GitCandy process hosts the Web UI, Git Smart HTTP, the built-in SSH server, Quartz jobs, and background entry points.
- Docker Compose using
ghcr.io/iotsharp/gitcandyoriotsharp/gitcandyfrom Docker Hub. - Linux x64 as a self-contained systemd service.
- Windows x64 as a self-contained Windows Service.
IIS deployment is not supported. Put a TLS reverse proxy in front of GitCandy when exposing the Web UI because Identity cookies are Secure-only.
cp .env.example .env
docker compose pull
docker compose up -d
docker compose psFor automatic HTTPS with Caddy, set GITCANDY_DOMAIN to a DNS name that points at the host and start the TLS overlay:
GITCANDY_DOMAIN=git.example.com docker compose -f docker-compose.yml -f docker-compose.tls.yml up -dFrom a source checkout, docker compose up --build -d automatically loads the build settings from docker-compose.override.yml. The Release Compose package omits that override and pulls the prebuilt image.
GitCandy checks for pending EF Core migrations and upgrades the SQLite/Identity database before Web, SSH, and background services start. Persistent state is stored in the gitcandy-data volume. HTTP and SSH default to host ports 8080 and 2222.
Images are published to both registries:
docker pull ghcr.io/iotsharp/gitcandy:latest
docker pull iotsharp/gitcandy:latestTagged GitHub Releases also contain Linux and Windows service packages, migration SQL, Compose files, and a loadable image archive.
The canonical repository page is /{namespace}/{repository}. Git Smart HTTP and LFS use /{namespace}/{repository}.git; SSH uses ssh://git@host:port/{namespace}/{repository}.git. A namespace belongs to an Identity user or team and is case-insensitively unique across both owner types.
Repository routes are a direct cutover. Legacy /git/{project}[.git], /Repository/... browsing routes, no-suffix HTTP/SSH Git remotes, and retained rename aliases are not served or redirected. They return not found; clients must update their remote to the current namespace/repository .git URL.
Name history defaults are configured under GitCandy:Namespaces:
{
"AliasRetentionDays": 365,
"RenameLimit": 3,
"RenameWindowDays": 7
}The rename limit applies to successful user/team namespace slug changes in a rolling window. Display-name changes do not create aliases or consume the limit. See the M10 migration record before upgrading an existing database.
Each readable repository exposes an Issue workspace at /{namespace}/{repository}/issues. Authenticated readers can create and discuss Issues; authors, assignees, repository owners, and administrators receive the corresponding edit and state-management permissions. Private repository Issue routes, references, and inbox notifications always recheck repository read access.
Issue descriptions and comments use restricted CommonMark with fenced code blocks and task lists. Raw HTML is disabled and rendered HTML is sanitized before storage. Repository owners can manage labels, milestones, assignees, relations, subscriptions, and discussion locks. Templates live at .gitcandy/ISSUE_TEMPLATE/{name}.md; default.md is used when no name is supplied. Successful Smart HTTP or built-in SSH pushes apply fixes #N, closes #N, and resolves #N from the default branch HEAD idempotently.
See the M11 migration record for schema, dependency, backup, and rollback details.
Signed-in users enter the private /me workspace, which keeps actionable Todos, followed activity, repositories needing attention, unified notifications, team context, and public recommendations separate from the public /{username} profile. /todos owns complete/restore/snooze state; reading /notifications never completes a Todo.
Public profiles expose only repositories, Stars, Packages, and public team context. /explore reads only anonymously accessible repositories from versioned recommendation snapshots, with deterministic fallback ordering. See the M12.7 acceptance record for privacy, metrics, migrations, and rollback details.
Canonical repositories expose /branches, /tags, and /contributors. Writers can delete non-default branches and tags through antiforgery-protected forms; ref validation and default-branch protection are enforced again by the Git service.
- Version-matched, anonymous help center:
/help/ - Liveness:
/health/live - Readiness:
/health/ready - OpenTelemetry tracing, metrics, and logging with optional OTLP export
- Optional migration-only command:
GitCandy --migrate - Detailed deployment, configuration, backup, restore, and rollback guide: docs/deployment.md
- Database provider notes: docs/database-providers.md
gitcandy.comon the existing sonnet.vip host: deploy/sonnet-vip/README.mdgitcandy.comproduction acceptance evidence: docs/migration/m12-6-sonnetdb-production-acceptance.md- Active product roadmap: ROADMAP.md
- Completed milestones and documentation index: docs/roadmap/completed-milestones.md
- Changes: CHANGES.md
.NET 10 SDK and Node.js 20 or later are required. Node bundles the application assets, while the pinned JekyllNet local tool generates /help from docs/help. Published deployments require neither Node, JekyllNet, nor a CDN.
dotnet tool restore
dotnet restore GitCandy.slnx
dotnet build GitCandy.slnx
dotnet test GitCandy.slnxThe client bundle and help site are rebuilt incrementally by MSBuild. Generated help HTML is copied only to build/publish output and must not be committed. See the M9 UI implementation record and the M15.5 help-center acceptance record for asset, documentation, validation, and rollback details.
GitCandy.slnx is the only active solution. The retired MVC5 source remains available through Git history, while its behavior baselines remain under docs/migration.
Thanks to sonnet.vip for providing the server resources used by the GitCandy deployment.
MIT. See LICENSE.md.