Turn GitHub activity into focused work: what needs your attention, why it matters, and what to do next.
Get started · Connect GitHub · Architecture · Development
Needly is a .NET 10 Blazor Web App for GitHub teams. It receives GitHub webhooks, turns relevant activity into durable actions, and presents an inbox organized around decisions and outcomes rather than an undifferentiated stream of notifications.
Note
Needly is under active development. GitHub integration is disabled by default, and a fresh clone can be built and run locally without GitHub credentials.
GitHub produces events. Teams need a clear queue of work. Needly bridges that gap by creating actions such as Review, Respond, Fix, Resolve, and Merge, then applying visibility, risk, and lifecycle rules for each user.
- Action Inbox: group and filter actionable work across pull requests and issues.
- GitHub App integration: use installation-scoped access and signed webhooks instead of personal access tokens.
- Action detection: identify review requests, unresolved feedback, CI failures, response-worthy mentions, and pull requests ready to merge.
- Durable event processing: persist webhook deliveries before acknowledging them, deduplicate delivery IDs, preserve ordering, retry transient failures, and recover after restart.
- Historical bootstrap: import existing open work after an installation is connected, with resumable repository-level progress.
- Saved Views: create reusable filters for action type, state, repository, organization, author, assignee, waiting time, bot involvement, and repository ownership.
- Automation Rules: automatically pin, archive, mute, snooze, or mark matching actions as FYI.
- Team-aware visibility: distinguish work assigned directly to you from work assigned to your teams.
- Risk and lifecycle controls: flag stale work, snooze actions, archive completed attention, and keep an undo history where appropriate.
- .NET 10 SDK
- Git
- PowerShell on Windows, or a shell capable of running the commands below
Clone the repository and enter its directory:
git clone https://github.com/kasuken/Needly.git
cd NeedlyRestore, build, and run the test suite:
dotnet restore
dotnet build .\Needly.sln --no-restore --no-incremental
dotnet test .\Needly.sln --no-restore --no-buildApply the existing EF Core migrations, then start the web application:
dotnet tool restore
dotnet tool run dotnet-ef database update `
--project .\Needly.Infrastructure\Needly.Infrastructure.csproj `
--startup-project .\Needly.Infrastructure\Needly.Infrastructure.csproj
dotnet run --project .\Needly.Web\Needly.Web.csprojOpen the URL printed by ASP.NET Core. The default Development profile uses SQL Server LocalDB.
Important
Migrations are explicit operations. Needly does not call EnsureCreated or apply migrations during application startup.
With GitHub integration disabled, the application can be used to verify the local shell and database setup. To connect GitHub:
- Register a GitHub App using the development or production manifest in
docs/. - Configure the App ID, slug, client credentials, private key, and webhook secret through user secrets or environment variables.
- Apply database migrations and start the Web project.
- Sign in at
/auth/loginand install the App for a personal account or organization. - Select the repositories Needly should watch in Settings. Historical bootstrap begins in the background while new webhooks continue through the same action pipeline.
The main routes are:
| Route | Purpose |
|---|---|
/ or /inbox |
Review open actions and apply lifecycle controls |
/views |
Manage saved filters and built-in team views |
/rules |
Manage ordered, per-user automation rules |
/settings |
Link GitHub installations and select repositories |
Needly requests read-only access for repository metadata, Actions, Contents, Issues, Pull requests, Checks, organization members, and user email addresses. It subscribes to installation, repository, issue, comment, pull request, review, check, workflow, member, team, and membership events.
Webhook requests are verified with HMAC-SHA256 before parsing or persistence. Accepted deliveries are stored durably, acknowledged with 202, and processed by bounded background workers. Duplicate delivery IDs are idempotent; unknown event names are retained and marked skipped.
Configure secrets with user secrets for local development:
dotnet user-secrets set 'GitHubApp:Enabled' 'true' --project .\Needly.Web\Needly.Web.csproj
dotnet user-secrets set 'GitHubApp:AppId' '<app-id>' --project .\Needly.Web\Needly.Web.csproj
dotnet user-secrets set 'GitHubApp:AppSlug' '<app-slug>' --project .\Needly.Web\Needly.Web.csproj
dotnet user-secrets set 'GitHubApp:ClientId' '<client-id>' --project .\Needly.Web\Needly.Web.csproj
dotnet user-secrets set 'GitHubApp:ClientSecret' '<client-secret>' --project .\Needly.Web\Needly.Web.csproj
dotnet user-secrets set 'GitHubApp:PrivateKey' '<private-key>' --project .\Needly.Web\Needly.Web.csproj
dotnet user-secrets set 'GitHubApp:WebhookSecret' '<webhook-secret>' --project .\Needly.Web\Needly.Web.csprojSee docs/github-app.md for callback and webhook URLs, manifests, permissions, event behavior, environment-variable names, and production guidance.
Warning
Never commit a private key, client secret, webhook secret, or user-secrets file. Use a secret manager in production. GitHub integration validates its configuration at startup when enabled.
The defaults live in Needly.Web/appsettings.json and can be overridden through the normal ASP.NET Core configuration providers.
| Setting | Default | Description |
|---|---|---|
GitHubActions:RequiredApprovals |
1 |
Latest approvals required before a Merge action can be created |
GitHubHistoricalBootstrap:MaxRepositoriesPerBatch |
25 |
Repositories imported per background batch |
GitHubHistoricalBootstrap:BatchInterval |
00:00:30 |
Delay between bootstrap batches |
GitHubApp:WebhookQueueCapacity |
1024 |
In-process webhook queue capacity |
GitHubApp:WebhookMaxAttempts |
5 |
Maximum transient processing attempts |
ActionRisk:ReviewWaitingThreshold |
08:00:00 |
Review wait time before it is marked at risk |
ActionRisk:InactivityThreshold |
3.00:00:00 |
Inactivity time before an open action is marked at risk |
Needly is split into focused .NET projects:
Needly.Web Blazor UI, authentication, routes, and HTTP endpoints
Needly.Application Application contracts and use-case services
Needly.Domain Actions, filters, rules, users, installations, and invariants
Needly.Infrastructure EF Core persistence, GitHub clients, detectors, workers, and migrations
Needly.Tests xUnit tests for domain, infrastructure, web, and GitHub behavior
The runtime flow is:
GitHub App -> signed webhook endpoint -> SQL Server RawEvent
-> background dispatcher
-> action detectors and rules
-> Action Inbox
SQL Server is the persistence provider: LocalDB for development, Azure SQL in production. The Infrastructure project owns the EF Core DbContext, design-time factory, and migrations.
Create a migration from the repository root with the Infrastructure project as both project and startup project:
dotnet tool restore
dotnet tool run dotnet-ef migrations add <MigrationName> `
--project .\Needly.Infrastructure\Needly.Infrastructure.csproj `
--startup-project .\Needly.Infrastructure\Needly.Infrastructure.csprojRun focused tests with the usual xUnit filters, or run the complete suite with dotnet test .\Needly.sln. The test project includes deterministic coverage for action detectors, webhook verification and recovery, GitHub API clients, persistence, saved views, rules, onboarding, and authentication.
- Saved Views and Rules share one versioned
ActionFiltercontract. Read docs/saved-views-and-rules.md for filter semantics, effects, ordering, and team behavior. - Merge readiness is intentionally conservative: incomplete API snapshots retract a Merge action, and the current REST lookups are limited to the first 100 reviews, statuses, and check runs. Details and caveats are documented in docs/github-app.md.
- Resolve action context reports an approximate unresolved review-comment count because GitHub REST webhook payloads do not expose authoritative GraphQL review-thread resolution state.
- Review risk is deterministic and derived from changed file paths and diff size only, never code quality: it degrades to Unknown, rather than Low, when the changed-file list cannot be fetched, and like merge readiness, the current REST lookup is limited to the first 100 changed files. Configurable signals, the default path-pattern-to-level mapping, and the Unknown fallback are documented in docs/github-app.md.
Local development uses EF Core 10 with SQL Server LocalDB. Both the Web host and the Infrastructure design-time factory default to:
Server=(localdb)\MSSQLLocalDB;Database=Needly;Trusted_Connection=True;TrustServerCertificate=True
Override the host with ConnectionStrings__Needly and the design-time factory with the NEEDLY_MIGRATIONS_CONNECTION environment variable.
Restore the repository-local EF tool and create or apply migrations from the repository root:
& 'C:\Program Files\dotnet\dotnet.exe' tool restore
& 'C:\Program Files\dotnet\dotnet.exe' tool run dotnet-ef migrations add <MigrationName> --project .\Needly.Infrastructure\Needly.Infrastructure.csproj --startup-project .\Needly.Infrastructure\Needly.Infrastructure.csproj
& 'C:\Program Files\dotnet\dotnet.exe' tool run dotnet-ef database update --project .\Needly.Infrastructure\Needly.Infrastructure.csproj --startup-project .\Needly.Infrastructure\Needly.Infrastructure.csprojDatabase migrations are explicit development and deployment operations. The application does not call EnsureCreated or apply migrations during startup.
Production runs in the Needly.Prod resource group (France Central), described by infra/main.bicep and infra/main.bicepparam:
| Resource | SKU | Notes |
|---|---|---|
needly-prodplan-linux App Service plan |
Linux B1 | Cheapest tier that supports custom domains and free managed certificates |
needly-prod-001 web app |
.NET 10 on Linux | System-assigned identity, WebSockets on for Blazor Server circuits |
needly-prod-001-server / -database |
Azure SQL Basic, 5 DTU, 2 GB | Microsoft Entra-only authentication, public endpoint plus firewall rules |
needly-prod-001 Application Insights + -law workspace |
Pay-as-you-go, 1 GB/day cap | |
id-needly-github-deploy |
User-assigned identity | GitHub OIDC federation, no stored credentials |
Provision or update the environment:
az deployment group create -g Needly.Prod --template-file .\infra\main.bicep --parameters .\infra\main.bicepparamThe database uses Entra-only authentication, so both managed identities need contained database users. infra/grant-database-access.sql is idempotent and is applied with the Entra SQL administrator's token:
$token = az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv
powershell.exe -File .\infra\Invoke-SqlScript.ps1 `
-ServerFqdn needly-prod-001-server.database.windows.net `
-Database needly-prod-001-database -AccessToken $token `
-ScriptPath .\infra\grant-database-access.sql.github/workflows/release-deploy.yml deploys on a published GitHub release or on manual dispatch. It builds and tests, verifies the EF model matches the migrations, applies migrations through a temporary firewall rule for the runner, publishes to App Service, and smoke tests /health/ready. The production environment holds AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_SUBSCRIPTION_ID; the federated credential is scoped to that environment, so deployments cannot run from other branches or forks.
GitHub App secrets are supplied as App Service application settings and are never committed:
az webapp config appsettings set -g Needly.Prod -n needly-prod-001 --settings `
GitHubApp__Enabled=true GitHubApp__AppId=<id> GitHubApp__ClientId=<id> `
GitHubApp__ClientSecret=<secret> GitHubApp__WebhookSecret=<secret> GitHubApp__PrivateKey="<pem>"To add a custom domain at no extra cost, point a CNAME at needly-prod-001.azurewebsites.net, add the asuid TXT verification record, then:
az webapp config hostname add -g Needly.Prod --webapp-name needly-prod-001 --hostname <domain>
az webapp config ssl create -g Needly.Prod --name needly-prod-001 --hostname <domain>
az webapp config ssl bind -g Needly.Prod --name needly-prod-001 --certificate-thumbprint <thumbprint> --ssl-type SNINeedly uses cookie authentication plus the GitHub App user authorization flow. GitHub integration is disabled by default, so a fresh clone starts without credentials. Registration, public callback URLs, and secrets remain owner-operated.
See docs/github-app.md for the required permissions, webhook events, development and production manifests, callback URLs, and local/production secret configuration.
Action behavior defaults are configured in appsettings.json: one approval is required for Merge actions, Review actions are marked at risk after more than eight hours waiting, and all open actions are marked at risk after more than three days without activity. See the GitHub App guide for override keys and readiness limitations.
After configuring a GitHub App and applying database migrations, sign in at /auth/login. The post-install setup URL returns to /github/setup, which links the installation to the signed-in Needly user and redirects to /settings.
After an installation is linked, Needly gradually bootstraps actions from the installation's current open pull requests and issues. The bootstrap persists synthetic events through the same durable processing pipeline used by webhooks, so existing review requests, unresolved feedback, failed checks, and conversations appear without waiting for new GitHub activity. Settings shows progress while this import is running and removes the notice after all selected repositories have been checked. The bootstrap is repository-scoped and resumable; by default, the worker processes up to 25 repositories per 30-second batch and reads at most ten pages from each GitHub endpoint. Configure these limits under GitHubHistoricalBootstrap, or set Enabled to false to disable backfill.
The first authenticated session opens a five-step MudBlazor wizard. It explains how Needly turns GitHub activity into actions, how to connect repositories, what the built-in focus sections mean, and how Saved Views, Rules, and action lifecycle controls shape the Inbox. The final step links directly to Settings or the Inbox.
Completing or explicitly skipping the introduction stores a completion timestamp on the Needly user, so the wizard does not reopen on later sessions or other devices.
Saved Views and automation Rules use one versioned filter contract. Views filter the authorized Inbox and provide live open counts; Rules apply ordered, per-user effects as GitHub events create or update actions. See docs/saved-views-and-rules.md for filter semantics, effects, ordering, team behavior, and persistence details.
