A modern, extensible wiki platform built with ASP.NET Core 8.0, featuring a plugin architecture and support for multiple database providers.
Full disclosure, this project was largely generated by AI for fun because I wanted to see how far I could go. There's some good, surely some bad, but the result still appears to be a functional wiki engine with some basic file management, tagging and categorization. I'm no C# developer and although I do have some C# knowledge I don't do it in a professional capacity and I haven't really touched it since before dotnet core was a thing. I did massage some things manually, but whether I did it best way... ¯\(ツ)/¯
It also includes plugin features that allow custom markdown extensions (table of contents plugin included), search providers (Lucene plugin included for more robust search), file storage providers (untested), authentication providers (OIDC plugin included). The intent is to support multiple databases as well since the project uses Entity Framework, though so far I've only tested sqlite locally
It has a flexible configuration system that allows both the just running the application and configuring it the way you want through the Settings page, or you can control every setting by setting docker environment variables which will lock the settings on the Settings page preventing them from being modified.
It has been a fun experience toying with AI code generation, and sometimes rather infuriating trying to coerce it to get back on track, often times having to fix some things manually and in all honesty, I've learned a few things along the way such as the distinction between a strategy pattern and factory pattern. There's definitely area's where AI can be extremely useful, and definitely areas where it falls short.
Why this project specifally for this AI project? Well a wiki came to mind as something I could use in my homelab to retain some form of knowledge base in case I need to do things again later. In my thought process I recalled Roadkill v2 wiki which I used years ago and really liked at the time, but roadkill v2 is quite stale and roadkill v3 as far as I can tell went dead around dotnet5.0 and remains incomplete. As such, I set out to make something similar and this is the result.
Why the name Squirrel? Well being inspired by Roadkill, I asked AI what is the opposite of roadkill? It obviously came back with nothing concrete but somewhere in the nonsense result it did refer to rodents, including squirrels, and my ADD brain thought Squirrels squirrel things away, so that was that.
If you like this project, and want to give some real human contribution, feel free to fork the repo and send me PR's. I'd really love to see this polished up as I'm sure there's lots of room for improvement
- Markdown-based content editing - extensible via plugins
- Site Customization (Menu, Navigation, Footer content)
- Home page can be replaced by simply creating a wiki page named "Home"
- Extensible plugin system (Search, File storage, Markdown, and Authentication)
- Search capabilities
- Tagging and Categories
- File Management
- Built in authentication (OpenID Connect plugin included)
- Multiple database support (SQLite, PostgreSQL, MySQL, MariaDB, SQL Server)
- Docker support for easy deployment
- Built-in caching (Memory or Redis)
docker build -t squirrel-wiki:latest .docker run -d \
--name squirrel-wiki \
-p 8080:8080 \
-v squirrel-data:/app/App_Data \
squirrel-wiki:latestAccess the wiki at http://localhost:8080
Default credentials:
- Username:
admin - Password:
Squirrel123!
docker run -d \
--name squirrel-wiki \
-p 8080:8080 \
-e SQUIRREL_DATABASE_PROVIDER=PostgreSQL \
-e SQUIRREL_DATABASE_CONNECTION_STRING="Host=postgres;Database=squirrelwiki;Username=squirrel;Password=yourpassword" \
-v squirrel-data:/app/App_Data \
squirrel-wiki:latestdocker run -d \
--name squirrel-wiki \
-p 8080:8080\
-e SQUIRREL_CACHE_PROVIDER=Redis \
-e SQUIRREL_REDIS_CONFIGURATION="redis:6379" \
-v squirrel-data:/app/App_Data \
squirrel-wiki:latestversion: '3.8'
services:
squirrel-wiki:
image: squirrel-wiki:latest
ports:
- "8080:80"
environment:
- SQUIRREL_DATABASE_PROVIDER=PostgreSQL
- SQUIRREL_DATABASE_CONNECTION_STRING=Host=postgres;Database=squirrelwiki;Username=squirrel;Password=yourpassword
- SQUIRREL_SITE_NAME=My Wiki
- SQUIRREL_SITE_URL=https://wiki.example.com
- SQUIRREL_CACHE_PROVIDER=Redis
- SQUIRREL_REDIS_CONFIGURATION=redis:6379
volumes:
- squirrel-data:/app/App_Data
depends_on:
- postgres
- redis
postgres:
image: postgres:16-alpine
environment:
- POSTGRES_DB=squirrelwiki
- POSTGRES_USER=squirrel
- POSTGRES_PASSWORD=yourpassword
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
volumes:
squirrel-data:
postgres-data:
redis-data:These settings are configured at startup and cannot be changed at runtime.
| Environment Variable | Description | Default | Allowed Values |
|---|---|---|---|
SQUIRREL_DATABASE_PROVIDER |
Database provider to use | SQLite |
SQLite, PostgreSQL, MySQL, MariaDB, SQLServer |
SQUIRREL_DATABASE_CONNECTION_STRING |
Database connection string | Data Source=App_Data/squirrel.db |
Valid connection string for chosen provider |
SQUIRREL_DATABASE_AUTO_MIGRATE |
Automatically apply database migrations on startup | true |
true, false |
SQUIRREL_DATABASE_SEED_DATA |
Seed initial data into the database on startup | true |
true, false |
SQLite:
Data Source=App_Data/squirrel.db
PostgreSQL:
Host=localhost;Database=squirrelwiki;Username=squirrel;Password=yourpassword
MySQL/MariaDB:
Server=localhost;Database=squirrelwiki;User=squirrel;Password=yourpassword
SQL Server:
Server=localhost;Database=squirrelwiki;User Id=squirrel;Password=yourpassword;TrustServerCertificate=True
These settings can be modified at runtime through the admin interface or environment variables.
| Environment Variable | Description | Default | Type |
|---|---|---|---|
SQUIRREL_SITE_NAME |
The name of your wiki site | Squirrel Wiki |
string |
SQUIRREL_SITE_URL |
The public URL of your wiki site | (empty) | URL |
SQUIRREL_DEFAULT_LANGUAGE |
Default language for the wiki interface | en |
en, es, fr, de, it |
SQUIRREL_TIMEZONE |
Default time zone for displaying dates and times (see available timezones) | UTC |
string |
| Environment Variable | Description | Default | Type |
|---|---|---|---|
SQUIRREL_ADMIN_USERNAME |
Default admin username for initial bootstrap | admin |
string |
SQUIRREL_ADMIN_PASSWORD |
Default admin password for initial bootstrap | Squirrel123! |
string (secret) |
SQUIRREL_ADMIN_EMAIL |
Default admin email address for initial bootstrap | admin@localhost |
string |
SQUIRREL_ADMIN_DISPLAYNAME |
Default admin display name for initial bootstrap | Administrator |
string |
SQUIRREL_ALLOW_ANONYMOUS_READING |
Whether unauthenticated users can read wiki pages | false |
boolean |
SQUIRREL_SESSION_TIMEOUT_MINUTES |
How long a user session remains active without activity | 480 |
integer (30-20160) |
SQUIRREL_MAX_LOGIN_ATTEMPTS |
Number of failed login attempts before account is locked | 5 |
integer (3-20) |
SQUIRREL_ACCOUNT_LOCK_DURATION_MINUTES |
How long an account remains locked after too many failed login attempts | 30 |
integer (5-1440) |
| Environment Variable | Description | Default | Type |
|---|---|---|---|
SQUIRREL_DEFAULT_PAGE_TEMPLATE |
Default markdown template for new pages | (empty) | string |
SQUIRREL_MAX_PAGE_TITLE_LENGTH |
Maximum number of characters allowed in page titles | 200 |
integer (25-500) |
SQUIRREL_ENABLE_PAGE_VERSIONING |
Whether to keep historical versions of pages | false |
boolean |
| Environment Variable | Description | Default | Type |
|---|---|---|---|
SQUIRREL_ENABLE_CACHING |
Whether to enable in-memory caching for improved performance | true |
boolean |
SQUIRREL_CACHE_DURATION_MINUTES |
How long cached items remain valid in memory | 60 |
integer (1-1440) |
SQUIRREL_ENABLE_RESPONSE_CACHING |
Whether to enable HTTP response caching for pages | true |
boolean |
SQUIRREL_RESPONSE_CACHE_DURATION_MINUTES |
How long HTTP responses are cached by browsers and proxies | 5 |
integer (1-60) |
SQUIRREL_CACHE_PROVIDER |
The caching provider to use | Memory |
Memory, Redis |
SQUIRREL_REDIS_CONFIGURATION |
Redis connection string | localhost:6379 |
string |
SQUIRREL_REDIS_INSTANCE_NAME |
Redis instance name prefix for cache keys | Squirrel_ |
string |
| Environment Variable | Description | Default | Type |
|---|---|---|---|
SQUIRREL_FILE_STORAGE_PATH |
Directory path where uploaded files are stored | App_Data/Files |
string |
SQUIRREL_FILE_MAX_SIZE_MB |
Maximum allowed file size for uploads in megabytes | 100 |
integer (1-2048) |
SQUIRREL_FILE_ALLOWED_EXTENSIONS |
Comma-separated list of allowed file extensions | .pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.md,.jpg,.jpeg,.png,.gif,.bmp,.svg,.zip,.rar,.7z |
string |
| Environment Variable | Description | Default | Type |
|---|---|---|---|
SQUIRREL_APP_DATA_PATH |
The directory path where application data is stored | App_Data |
string |
SQUIRREL_SEED_DATA_FILE_PATH |
The path to a custom seed data YAML file | (empty) | string |
Squirrel.Wiki includes three built-in plugins that can be enabled and configured through environment variables.
Provides OpenID Connect / OAuth 2.0 authentication support.
| Environment Variable | Description | Required | Default |
|---|---|---|---|
PLUGIN_SQUIRREL_AUTH_OIDC_ENABLED |
Enable the OIDC authentication plugin | No | false |
PLUGIN_SQUIRREL_AUTH_OIDC_AUTHORITY |
The URL of the OpenID Connect provider | Yes | - |
PLUGIN_SQUIRREL_AUTH_OIDC_CLIENTID |
The client ID registered with the OIDC provider | Yes | - |
PLUGIN_SQUIRREL_AUTH_OIDC_CLIENTSECRET |
The client secret for authentication | Yes | - |
PLUGIN_SQUIRREL_AUTH_OIDC_SCOPE |
The OAuth scopes to request (space-separated) | No | openid profile email |
PLUGIN_SQUIRREL_AUTH_OIDC_USERNAME_CLAIM |
The claim to use for the username | No | preferred_username |
PLUGIN_SQUIRREL_AUTH_OIDC_EMAIL_CLAIM |
The claim to use for the email address | No | email |
PLUGIN_SQUIRREL_AUTH_OIDC_DISPLAY_NAME_CLAIM |
The claim to use for the display name | No | name |
PLUGIN_SQUIRREL_AUTH_OIDC_GROUPS_CLAIM |
The claim that contains group memberships | No | groups |
PLUGIN_SQUIRREL_AUTH_OIDC_ADMIN_GROUP |
The group name that grants admin privileges | No | squirrel-admins |
PLUGIN_SQUIRREL_AUTH_OIDC_EDITOR_GROUP |
The group name that grants editor privileges | No | squirrel-editors |
PLUGIN_SQUIRREL_AUTH_OIDC_AUTO_CREATE_USERS |
Automatically create user accounts on first login | No | true |
PLUGIN_SQUIRREL_AUTH_OIDC_REQUIRE_HTTPS_METADATA |
Require HTTPS for metadata endpoint | No | true |
docker run -d \
--name squirrel-wiki \
-p 8080:8080\
-e PLUGIN_SQUIRREL_AUTH_OIDC_ENABLED=true \
-e PLUGIN_SQUIRREL_AUTH_OIDC_AUTHORITY=https://keycloak.example.com/realms/myrealm \
-e PLUGIN_SQUIRREL_AUTH_OIDC_CLIENTID=squirrel-wiki \
-e PLUGIN_SQUIRREL_AUTH_OIDC_CLIENTSECRET=your-client-secret \
-e PLUGIN_SQUIRREL_AUTH_OIDC_SCOPE="openid profile email" \
-e PLUGIN_SQUIRREL_AUTH_OIDC_ADMIN_GROUP=wiki-admins \
-e PLUGIN_SQUIRREL_AUTH_OIDC_EDITOR_GROUP=wiki-editors \
-v squirrel-data:/app/App_Data \
squirrel-wiki:latestHigh-performance full-text search using Lucene.NET.
| Environment Variable | Description | Required | Default |
|---|---|---|---|
PLUGIN_SQUIRREL_WIKI_PLUGINS_LUCENE_ENABLED |
Enable the Lucene search plugin | No | true |
PLUGIN_LUCENE_INDEX_PATH |
Path to the Lucene search index directory | No | SearchIndex |
The Lucene plugin is included by default and provides advanced search features including:
- Fast full-text search
- Fuzzy matching
- Field boosting
- Relevance scoring
- Search suggestions
- Similarity search
docker run -d \
--name squirrel-wiki \
-p 8080:8080\
-e PLUGIN_SQUIRREL_WIKI_PLUGINS_LUCENE_ENABLED=true \
-e PLUGIN_LUCENE_INDEX_PATH=SearchIndex \
-v squirrel-data:/app/App_Data \
squirrel-wiki:latestGenerates a table of contents from markdown headings using the {{toc}} placeholder.
| Environment Variable | Description | Required | Default |
|---|---|---|---|
PLUGIN_SQUIRREL_WIKI_PLUGINS_MARKDOWN_TABLEOFCONTENTS_ENABLED |
Enable the Table of Contents plugin | No | true |
PLUGIN_TOC_MAX_DEPTH |
Maximum heading level to include in TOC (1-6) | No | 3 |
The Table of Contents plugin is included by default. To use it, enable the plugin and simply add {{toc}} anywhere in your markdown content where you want the table of contents to appear.
{{toc}}
# Introduction
This is the introduction...
# Main Content
## Subsection 1
Content here...
## Subsection 2
More content...When running in Docker, mount the following volumes to persist data:
/app/App_Data- Application data including database, search index, and uploaded files/app/Plugins/CustomPluginName- Plugin directory (if you want to add a custom plugin)
The application exposes health check endpoints:
/health- Basic health check/health/ready- Readiness check (includes database connectivity)
Logs are written to:
- Console (stdout/stderr)
/app/logsdirectory (if volume mounted)
Configure logging level via environment variable:
-e Logging__LogLevel__Default=Information# Restore dependencies
dotnet restore
# Build the solution
dotnet build
# Run the web application
dotnet run --project Squirrel.Wiki.Web