Self-hosted service
LeslieLeung/glean avatar
LeslieLeung/glean

Glean: the default deployment publishes its admin password, and the client IP headers it trusts default to nobody

A self-hosted RSS reader and personal knowledge management tool.

869 stars63 forksTypeScriptAGPL-3.0

At a glance

What is it?
A self-hosted RSS reader and knowledge manager shipped as seven containers, including a vector database for features still marked as work in progress. The documentation hands you a default admin password and a placeholder signing secret, and the environment example tells you to generate that secret one way while the page shows you another.
Who is it for?
Use Glean if you want to own your feed database and read it somewhere you control, and be prepared to harden it before it touches a network you care about. Five things to do first.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The documented deployment starts with a published admin password

The quick start is one download and one compose command:

bash
# Download docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/LeslieLeung/glean/main/docker-compose.yml -o docker-compose.yml

# Start Glean (full deployment with Milvus)
docker compose up -d

The same block then tells you the admin dashboard is on port 3001 with the credentials admin and a password that is printed on the page, and a following section repeats them as the default account created automatically at startup. The configuration table marks three values as must change in production: the signing secret, the database password, which defaults to the same word as the database name, and the admin password. The compose file repeats the admin default in its own header comment, so the credential is in three places, and nothing in the startup path stops the stack from coming up with all three unchanged. There is an escape hatch for the account at least: a setting disables automatic creation, and a follow-up command runs a script inside the already running backend container to create the administrator by hand.

Two different commands for generating the same secret

The page shows you how to write your own environment file before the first start:

bash
# Set custom admin credentials in .env
cat > .env << EOF
ADMIN_USERNAME=admin
ADMIN_PASSWORD=YourSecurePassword123!
SECRET_KEY=$(openssl rand -base64 32)
EOF

The example environment file that the same page tells you to download generates that secret a different way, with a hexadecimal flag rather than a base64 one, and both are thirty two bytes. Neither is wrong, and both produce a strong value, but a reader who copies the page and then compares it against the file they were told to download has to work out which one they are supposed to follow. The example file also ships a literal placeholder as the default value rather than an empty field, which means a deployment that copies the file and starts has a signing secret, and it is a sentence in English that anyone can read.

The default client IP comes from two headers anyone can set

The authentication section of the environment file has an OIDC block that is more carefully specified than the rest of the defaults. Scopes, a discovery URL that can be auto-discovered from the issuer, a JWKS cache lifetime of a day, a rate limit window of sixty seconds, and separate limits of thirty on the authorize and callback endpoints. Client identity is resolved from a priority-ordered list of headers, and the default list names a CDN connecting-IP header first and a reverse proxy real-IP header second. Right next to it sits a setting for the proxy addresses those headers should be trusted from, and it is empty by default. So the rate limiter, which is the only abuse control described, keys on a value the client can influence unless that list is filled in.

The default OIDC callback points at a development port

The provider name in the example is a large consumer identity provider, the issuer is that provider's account endpoint, and the client id and secret are empty. The redirect URI is set to a localhost address on port 3000 with a callback path. That is the development port for the web app: the deployment table puts the production web interface on port 80, and the makefile help text names port 3000 for the same app when you start it locally. So the default OIDC configuration describes a developer machine, and a self-hosted deployment has to notice that field and change it before a single login works. Local password authentication is enabled by default as well, and so is open registration, which means an instance left with its defaults accepts signups from anyone who can reach the web port.

Seven containers by default, one for features still in progress

The full deployment is a web app, an admin dashboard, a backend API, a background worker, PostgreSQL, Redis, and Milvus, the last named as the vector database for smart recommendations and preference learning and tagged with a phase number. The planned features list marks smart recommendations, the rule engine, and the AI features as work in progress, with the AI ones described as bring your own key. So the one command default starts a vector database to serve features the same page describes as unfinished. A lite compose file exists for exactly this case, and it is the one to use if you only want the reader, which means the default and the lean option are one flag apart in cost and several in persistence. The queue between them is a Redis instance holding append-only data, and the worker behind it is the component that fetches feeds and runs the cleanup, so removing the vector store does not remove any of the scheduled work.

Two package managers, one lock file too many, and two identical scripts

The root manifest is a task runner rather than an application: it is private, it declares one development dependency, and every script shells out to make. Two of its scripts, the general development one and the one named for all services, are the same long command with the same four named processes and the same colours. Four more scripts exist purely as single-service aliases. Alongside it sit two lock files, one per package manager, while the documented development quick start uses one of them and the makefile has separate targets for installing backend, frontend and root dependencies. A small version-syncing script also exists in the root scripts directory, which is the usual sign that several manifests carry the version number by hand.

Five compose files, two of them documented

The root holds five compose files: the full one, the lite one, an override, a development variant and a test variant. The header comment inside the full file explains the intended combinations, which is to use the lite file alone, or layer the override on top for local development, and the makefile adds a test database on its own port. So there are at least four legitimate ways to bring this stack up and the page walks through two of them. The pre-commit configuration covers the same ground on the code side, with a formatter, a linter and a type checker for the backend, two tools for the frontend, and generic checks for whitespace, line endings and configuration file syntax.

The pre-release caveat names a desktop app the features never list

The section on trying unreleased builds explains how to pin a prerelease image tag, either in the environment file or inline on the compose command, and then warns that prerelease versions will not trigger automatic updates for Electron applications. The feature list contains no desktop application, and the deployment list has no desktop application either. One does exist somewhere else in the repository: the makefile help text has a target for starting an Electron desktop app. The warning also reveals that an updater exists and is client-side, which is worth knowing before you rely on automatic updates for the reader you actually run. The example prerelease tag is a third minor version ahead of the newest stable release.

Editorial conclusion

Use Glean if you want to own your feed database and read it somewhere you control, and be prepared to harden it before it touches a network you care about. Five things to do first. Change the admin password, the database password and the signing secret before the first start, because all three defaults are printed in the project's own instructions and the compose file repeats two of them. Set the trusted proxy list, because the default client IP resolution reads two forwarding headers that anyone can set unless you restrict where they are believed from, and those values feed the authentication rate limits. Decide whether you want the vector database: it is in the one command default, and the features it serves are still marked as work in progress. Regenerate the signing secret yourself rather than copying either example, since the page and the environment file disagree on how to generate it. And read the pre-release caveat about desktop updates before you assume the automated updater covers the platform you are running, because the note names a desktop shell the feature list never mentions.

Frequently asked questions

What are the default Glean admin credentials?

The admin dashboard is created automatically on first start with the username admin and the password Admin123!. The documentation repeats this default in the quick start, in the credentials section and in the compose file's own comment.

Which Glean environment variables must change before production?

The signing secret, the database password and the admin password. The example file also shows a literal placeholder as the default signing secret, and the settings table marks each of the three as needing a change.

What does the lite Glean deployment leave out?

The vector database. The full one-command deployment starts seven services including Milvus for recommendations and preference learning, while the lite compose file drops it for anyone who does not need those phase three features.

How does Glean determine the client IP for rate limiting?

From a priority-ordered list of forwarding headers, which defaults to a CDN connecting-IP header followed by a reverse proxy real-IP header. The separate setting for which proxy addresses to trust from those headers is empty by default.

How often does Glean refresh feeds?

Every fifteen minutes, according to the background sync entry in the feature list. That interval is stated as a fixed figure there rather than as a setting in the configuration table.

Official sources

  1. LeslieLeung/glean on GitHub
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/leslieleung-glean.svg)](https://hysenlabs.com/projects/leslieleung-glean)