# 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 ```text 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: ```sh 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: ```powershell .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: ```sh . .venv/bin/activate PYTHONPATH=. uvicorn backend.app.main:app --reload --host 127.0.0.1 --port 8000 ``` Open these URLs: - Public feed: - User feed: - Profile settings: - Admin page: - Web login: - Health check: - OpenAPI documentation: 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 ```sh . .venv/bin/activate PYTHONPATH=. pytest backend/tests -q ``` To ensure warnings are clean as well: ```sh 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: ```sh 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: ```sh docker compose up --build ``` The services are available at: - LinkLog through Traefik: - Direct application port: - Traefik dashboard: 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: ```sh docker compose down ``` Stop the stack and delete the named database volume: ```sh 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 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: ```sh 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: ```sh 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: ```text 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: ```sh 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.