Browser-free HTTP registration for ordinary Forgejo accounts using usernames, passwords and SSH keys.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Pavel Voronin a70650c912
All checks were successful
Tests / test (push) Successful in 7s
ci: run registration tests in isolated Docker jobs
2026-09-11 09:56:34 +00:00
.forgejo/workflows ci: run registration tests in isolated Docker jobs 2026-09-11 09:56:34 +00:00
deploy feat: add HTTP registration for Forgejo accounts 2026-09-11 17:31:47 +08:00
tests fix: require email for account registration 2026-09-11 17:55:07 +08:00
.gitignore feat: add HTTP registration for Forgejo accounts 2026-09-11 17:31:47 +08:00
DEPLOYMENT.md fix: require email for account registration 2026-09-11 17:55:07 +08:00
README.md docs: simplify SSH instructions 2026-09-11 17:55:44 +08:00
service.py fix: require email for account registration 2026-09-11 17:55:07 +08:00

forgejo-registration

HTTP registration for ordinary Forgejo accounts, for humans and agents alike. Requires Python 3.11+ and only the standard library. No browser or special client is required by the service.

Instance: https://code.kolpaque.dev. Registration: POST https://code.kolpaque.dev/registration.

Register

Generate an SSH key with ssh-keygen -t ed25519. Save the following JSON to a local file with permissions 0600, replacing the password with your own unique password and the key with your public key:

{
  "username": "your-login",
  "email": "[email protected]",
  "password": "REPLACE-with-Your-Unique-Password-123!",
  "ssh_key": "ssh-ed25519 AAAA..."
}
chmod 600 registration.json
curl --fail-with-body -H 'Content-Type: application/json' \
  --data-binary @registration.json https://code.kolpaque.dev/registration

Usernames must contain 239 characters, starting with a lowercase ASCII letter, followed by lowercase letters, digits, underscores or hyphens. Passwords must contain 16128 characters, including a lowercase letter, an uppercase letter, a digit and a special character. Forgejo also enforces its own password policy. Ed25519, RSA and ECDSA keys are supported; Forgejo performs the final key format and length validation.

Successful creation returns HTTP 201; retrying with the same credentials returns HTTP 200. Responses contain only username, profile_url, api_url and ssh_key_added. Passwords and administrative tokens are never returned. The required email field sets the account email at creation. Supply a plain ASCII email address (no display name); Forgejo performs final validation and uniqueness checks. No email confirmation or notification is sent by this service, so the address is not proof of mailbox ownership. Missing or empty email addresses are rejected with HTTP 400; no placeholder address is generated. A retry must match the existing account email; registration never changes an existing email or links accounts by email. Use ordinary account settings to change an email.

Git and API access

Push a repository over SSH:

git remote add origin ssh://[email protected]:2222/your-login/my-project.git
git push -u origin HEAD:main

The first push creates a public repository. Branches are writable by their owner by default; this does not grant other users write access. To create a private repository, create it first through the API or web interface with private: true. Existing private repositories and protections are unchanged.

Check the SSH host-key fingerprint in DEPLOYMENT.md on first connection.

# curl prompts for the password; agents can use a protected netrc file.
curl --fail-with-body --user your-login https://code.kolpaque.dev/api/v1/user

# Create a personal access token and save the response to a protected file.
umask 077
curl --fail-with-body --user your-login \
  -H 'Content-Type: application/json' \
  --data '{"name":"my-agent","scopes":["write:repository","read:user"]}' \
  https://code.kolpaque.dev/api/v1/users/your-login/tokens > token.json

Use the token from the sha1 field in Authorization: token .... It is displayed only when created. If lost, create a replacement and revoke the old token. For accounts with 2FA, use the standard X-Forgejo-OTP header when creating a token; registration retries do not bypass 2FA. Git HTTPS is also available at https://code.kolpaque.dev/your-login/my-project.git. See Swagger and the API schema.

Errors and retries

HTTP Error Action
400 invalid_*, expected_*, password_requires_* Correct the JSON, username, email, password or key. Do not send extra fields.
409 username_unavailable_or_credentials_invalid The username is taken, credentials are incorrect or the account is unavailable to this flow.
409 email_mismatch_use_account_settings Retry with the original email, or use account settings to change it.
422 account_creation_failed Forgejo rejected account creation. Check username, email availability and password requirements.
422 account_exists_key_rejected The account exists, but the key is invalid or already in use. Retry with the same password and a corrected or new key.
503 account_exists_retry_with_same_credentials, forgejo_unavailable, temporarily_unavailable Retry with the same credentials using exponential backoff.
413 / 415 Body size or content type limit Send JSON, no larger than 12 KB.

A disconnected request may have an unknown outcome; retrying is safe. Accounts are intentionally retained after key attachment fails because they may already be in use. The service verifies the password through /user, then attaches the key through the user-scoped /user/keys endpoint. It does not call the administrative key API. A different key supplied with valid credentials is added without deleting existing keys. A manually deleted account may be registered again.

Deployment

  1. Set up Forgejo, an HTTPS reverse proxy and Python 3.11+.
  2. Create a server-side administrative token with scopes write:admin,read:user. Store it outside the repository in a separate file with permissions 0600. The service does not need the administrator's password.
  3. Copy the code and adapt deploy/forgejo-registration.service and deploy/config.env.example. Install the unit in ~/.config/systemd/user/, run systemctl --user daemon-reload, then systemctl --user enable --now forgejo-registration.
  4. Route only /registration and /registration/* to 127.0.0.1:3102, preserving the prefix. Limit bodies to 12 KB. Route all other paths to Forgejo.
  5. Exempt machine endpoints from browser challenges while retaining Forgejo authentication and other applicable rules. A Cloudflare rule template is provided in deploy/cloudflare-rule.json.
  6. Enable Forgejo's built-in SSH server and direct access to its selected port. A normal Cloudflare Tunnel does not replace public SSH access.

Example Caddy configuration:

forge.example {
    @registration path /registration /registration/*
    handle @registration {
        request_body {
            max_size 12KB
        }
        reverse_proxy 127.0.0.1:3102
    }
    handle {
        reverse_proxy 127.0.0.1:3100
    }
}

The internal URL must point to a trusted Forgejo instance using loopback HTTP or verified HTTPS. Redirects are rejected to prevent forwarding credentials to another server. The service does not log request bodies, headers or passwords. It limits concurrency to 16 threads and applies body size limits and timeouts. Registration requests are serialized within each process; Forgejo guarantees uniqueness across instances, but concurrent key attachment may require a retry.

Registration is open: one person can create multiple accounts, so per-account quotas do not limit their combined usage. Do not enable proxy logging of request bodies or Authorization headers. The /registration/health endpoint checks the process only; end-to-end monitoring must check Forgejo separately.

Verification

python3 -m unittest discover -s tests -v

Tests cover retries, concurrency, partial registration, incorrect passwords, administrative fields and accounts, and path injection. Live deployment checks are documented in DEPLOYMENT.md.

The installed Forgejo 16.0.4 API exposes POST /admin/users and POST /user/keys, but no public JSON signup endpoint. HTML signup uses a browser flow; this service provides a stable HTTP contract. References: installed API schema, Forgejo API documentation.