Files
Link-Log/README.md
T

9.3 KiB

LinkLog

LinkLog is a Firefox extension and Python web service for saving links with a title, comment, timestamp, and tracking parameters removed. The service stores links in SQLite and can publish them through plugins, including Mastodon.

Project Layout

backend/       FastAPI application, services, database, and tests
frontend/      Jinja templates and browser-side assets
webextension/  Firefox Manifest V3 extension
Dockerfile     Backend container image
docker-compose.yml  App plus Traefik for local proxying
REQUIREMENTS.md    Product requirements
VIBE/           Conversation and prompt logs

Requirements

For local development:

  • macOS, Linux, or Windows with Python 3.11+
  • Firefox for installing the extension
  • Docker Desktop and Docker Compose v2 for the container workflow

The backend currently uses FastAPI, uvicorn, SQLite, and Pydantic. httpx2 is included for the Starlette-compatible test client. Jinja2 is included for server-rendered HTML templates.

Local Installation

From the repository root:

python3.11 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r backend/requirements.txt

On Windows PowerShell, activate the environment with:

.venv\Scripts\Activate.ps1

The SQLite database is created automatically at backend/data/linklog.db when the application starts or when the auth router is imported. The starter accounts are:

The database schema is versioned with SQLite PRAGMA user_version. Application startup applies all pending migrations in order, so updating the application does not require deleting an existing database. New schema changes should be added as a new numbered migration in backend/app/database.py; existing migration entries must remain unchanged.

Username Password Role
alice secret123 administrator
bob secret123 standard user

These credentials are for development only. Change the authentication and seeding design before deploying publicly.

Run The Backend

Activate the virtual environment, then run uvicorn from the repository root:

. .venv/bin/activate
PYTHONPATH=. uvicorn backend.app.main:app --reload --host 127.0.0.1 --port 8000

Open these URLs:

The browser extension defaults to http://localhost:8000.

Appending a username to the root URL, such as /alice, opens that user's public feed and profile information.

Configuration APIs require a bearer token returned by the login endpoint. User configuration uses the identity in that token. Plugin administration additionally requires an administrator account; the development alice account is seeded as an administrator, while bob is a standard user.

On the profile page, the authenticated username is displayed as read-only. Users can upload a PNG, JPEG, GIF, or WebP avatar up to 2 MB; uploaded files are stored in the persistent data volume and served by the application. Bio and email fields remain empty until the user provides values. Mastodon settings default to the mastodon.social instance and the From my #LinkLog: " post prefix.

Run Tests

. .venv/bin/activate
PYTHONPATH=. pytest backend/tests -q

To ensure warnings are clean as well:

PYTHONPATH=. PYTHONWARNINGS=error pytest backend/tests -q

Install The Firefox Extension For Development

The extension is an unpacked Firefox extension. No build step is required.

  1. Start the backend locally.
  2. Open Firefox and visit about:debugging#/runtime/this-firefox.
  3. Select Load Temporary Add-on.
  4. Choose webextension/manifest.json.
  5. Open the LinkLog extension options and enter:
    • Backend URL: http://localhost:8000
    • Username: alice
    • Password: secret123
  6. Save the settings and login.
  7. Open a webpage, select the LinkLog toolbar button, review the title and URL, add a comment, and submit it.

Temporary extensions are removed when Firefox restarts. Reload the extension from about:debugging after changing its files.

Docker And Traefik

Create the Docker environment file before starting the stack:

cp .env.example .env

Edit .env and replace LINKLOG_SECRET_KEY with a long random value. Docker Compose automatically reads .env from the repository root. The committed .env.example contains safe defaults and placeholders; the real .env is ignored by Git.

The main configurable values are:

Variable Purpose Default
LINKLOG_SECRET_KEY token signing/security secret required in Docker
LINKLOG_DATABASE_PATH SQLite file path inside the container /app/backend/data/linklog.db
LINKLOG_TOKEN_EXPIRY_DAYS access-token lifetime 30
LINKLOG_TRACKING_PARAMS comma-separated tracking parameters built-in list
TRAEFIK_HOST hostname routed by Traefik localhost
TRAEFIK_HTTP_PORT host port for the application proxy 80
TRAEFIK_DASHBOARD_PORT host port for the dashboard 8080
TRAEFIK_API_INSECURE enable the local dashboard API true
APP_PORT direct host port for FastAPI 8000

The full set of supported variables is listed in .env.example. Application variables are passed into the container by Compose; Docker and Traefik variables are used by Compose itself.

Build and start the application and local Traefik proxy:

docker compose up --build

The services are available at:

The Compose configuration routes the hostname localhost through Traefik. The SQLite database is stored in the named Docker volume linklog_data, mounted at /app/backend/data.

Stop the stack without deleting its database:

docker compose down

Stop the stack and delete the named database volume:

docker compose down -v

For a real deployment, replace the localhost router rule, configure TLS, protect the Traefik dashboard, avoid exposing the direct application port, and provide production secrets and authentication. The included Compose file is a local/prototype deployment scaffold, not a production security configuration.

Mastodon Configuration

After logging in, open http://localhost:8000/profile and save the Mastodon settings:

  • Instance: hostname or URL such as mastodon.social or https://mastodon.social
  • Access token: a Mastodon API token with permission to create statuses
  • Post prefix: text placed immediately before the link; defaults to From my #LinkLog: "

Enable the plugin from the admin API or the admin page. New links are saved first and then posted to the configured instance at /api/v1/statuses. A Mastodon network failure does not undo the saved link.

Useful API Calls

Login:

curl -X POST http://localhost:8000/api/auth/login \\
  -H 'Content-Type: application/json' \\
  -d '{"username":"alice","password":"secret123"}'

Submit a link using the returned access token:

curl -X POST http://localhost:8000/api/links \\
  -H 'Content-Type: application/json' \\
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \\
  -d '{"title":"Example","url":"https://example.com","comment":"Worth reading"}'

The public feed is available at:

GET http://localhost:8000/api/public/feed

When a request includes a valid bearer token, entries owned by that authenticated user include edit permission and show an inline Edit action in the feed. The update endpoint is PUT /api/links/{link_id} and rejects edits from other users. Feed items also expose is_owner; it is true only for entries owned by the authenticated user and false for anonymous viewers or other users.

Admin plugin requests must include the administrator's token:

curl http://localhost:8000/api/admin/plugins \\
  -H 'Authorization: Bearer YOUR_ADMIN_ACCESS_TOKEN'

Administrators can manage user privileges from the admin page. The Administrator checkbox sends PUT /api/admin/users/{user_id} with {"is_admin": true} or {"is_admin": false}. The API rejects any change that would leave the system without an administrator.

Troubleshooting

  • ModuleNotFoundError: activate .venv and rerun python -m pip install -r backend/requirements.txt.
  • Jinja2 import error in Docker: rebuild the image with docker compose up --build; the runtime dependency is declared in backend/requirements.txt.
  • Extension cannot login: confirm the backend is running, use the exact backend origin without a trailing API path, and check the browser console for blocked requests.
  • Port 8000 is occupied: run uvicorn with another port and update the extension backend URL, for example --port 8001.
  • Port 80 or 8080 is occupied: change the host-side ports in docker-compose.yml.
  • Stale development data: stop the backend and remove backend/data/linklog.db, or run docker compose down -v for the container volume.
  • Schema migration issue: inspect the database version with sqlite3 backend/data/linklog.db 'PRAGMA user_version;' and restart the application to apply pending migrations.