# Rin: an edge blog whose first registered user becomes admin, with admin123 in the example env

> Rin is a serverless blog built from four Cloudflare products, with a command line wrapper doing most of the work and a single bun lockfile. Its documentation is thorough about deployment and conspicuously quiet about two defaults: the first account to register becomes the administrator, and the example environment file ships that administrator's username and password in plain text, alongside a placeholder signing secret.

**openRin/Rin** — 🌸Publish faster with an edge-native blog powered by Cloudflare Workers, D1, and R2

- Repository: https://github.com/openRin/Rin
- Website: https://docs.openrin.org
- Stars: 3,096 · Forks: 2,523
- Language: TypeScript
- License: MIT
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/openrin-rin

## Four Cloudflare products, one domain, no server

The quick start is four commands and one file to edit:

```bash
# 1. Clone the repository
git clone https://github.com/openRin/Rin.git && cd Rin

# 2. Install dependencies
bun install

# 3. Configure environment variables
cp .env.example .env.local
# Edit .env.local with your own configuration

# 4. Start the development server
bun run dev
```

Then you open the default local development port to start. The architecture is four named products and nothing else, which is the whole pitch. Pages hosts the frontend, Workers run the serverless functions, D1 holds the SQLite database and R2 stores objects. The claim is that you deploy a personal blog by pointing a domain at Cloudflare, with no server management involved. The feature list is consistent with that split rather than with a traditional stack: image handling targets S3-compatible storage explicitly so R2 works, with automatic link generation after an upload, and the backend holds the logic that cannot live in the browser.

## The first account to register becomes the administrator

Authentication is GitHub OAuth, and the role assignment is described in one sentence that deserves attention. The first registered user becomes an administrator, and subsequent users join as regular members. That is a first-come arrangement rather than a configured one: there is no bootstrap token in the documentation and no environment variable naming an initial owner. The example environment file shows the alternative path, where an administrator username and password can be set for account-and-password login, and notes that if GitHub OAuth is not configured then only that route is available. So the deployment has two ways to get an owner, and the one most people will reach for first, registering, hands the administrator role to whoever arrives earliest on an instance that is reachable from the internet.

## The example environment file ships an administrator password

The example environment file is where the defaults live, and two of them are worth reading before you copy it. It sets an administrator username of admin and an administrator password of admin123, with a comment naming them as the defaults. It also sets a signing secret to a placeholder string of the obvious kind, alongside placeholder storage access keys and a placeholder storage bucket name. The file is organised, with commented sections for site configuration, backend settings, storage, webhooks, RSS, sensitive configuration and the database, and it documents a precedence order: the settings page beats environment variables, which beat defaults. That last line is the useful one, because it means a value changed in the interface survives a redeploy, and it also means an attacker who reaches the settings page can change configuration you thought was pinned.

## The example environment file is annotated in Chinese while the page is in English

The repository carries two languages of documentation and they are not paired the way you would expect. The main page and the contributing guide exist in English, and both have Chinese counterparts beside them. The example environment file, however, is annotated entirely in Chinese, with no English version, and it is the file every new user copies first. That file also carries the most operationally important information in the repository: the storage endpoint and bucket placeholders, the path-style flag, the cache storage mode, the frontend URL that is used to build absolute links in the sitemap and the robots file with a fallback to the request origin when it is empty, and the RSS and pagination defaults. All of it arrives in a language the default readme assumes you do not read.

## Deploy creates the database and runs migrations in the same command

The deployment story is the most developed part of the documentation and it is worth reading as a sequence rather than a list. Two environment values are required, a Cloudflare API token and an account identifier, and four are optional with defaults: a worker name, a pages name, a database name and a bucket name. The bucket is the interesting one, because if it is set the deploy derives the matching storage settings from it, and if it is left unset no bucket is selected at all, so images have nowhere to go unless you configure storage yourself. Then the script does five things in order: create the database if it does not exist, derive the storage settings only when the bucket name was given, deploy the backend to Workers, build and deploy the frontend to Pages, and run the database migrations.

## Building the server is a dry-run deploy, and the migrations are a separate script

Two script definitions are worth reading closely because their names overpromise. The build script for the server creates an output directory and then runs the deployment tool with a dry-run flag and an output directory, so the server build produces an artefact from a deployment that was never sent anywhere, which is fine for checking that it compiles and useless as a build output to run. The frontend build is an ordinary filtered build in the client workspace. Database work is exposed as three separate scripts: one that runs a migrate command through the project's own command line wrapper, one that generates a client inside the server workspace, and one named for a fix that targets a single column, which reads like a one-off migration for a field that was added at the top of a list.

## Two scripts named for hot reloading run the same command

The script table has a small blemish worth reporting. There is a script for starting the development server and another named for a hot variant, and both expand to the same command through the project's own command line wrapper. So the hot-reloading entry point does nothing the plain one does not already do, and anyone wiring it into a script or a documentation snippet is relying on a name that promises more than the manifest delivers. Almost every other script in the root manifest does go through that wrapper, with a mode flag selecting the client, the server or both, and a separate script for the initial setup step, which is the pattern that makes a single wrapper plausible in the first place.

## Blogroll links are checked every twenty minutes, on port 11498

The blogroll feature adds links to other people's blogs and the backend checks whether each link is still available every twenty minutes, which is the only periodic job described anywhere on the page. That job is also the reason there is an unusual development script. One script runs the worker locally in the platform's scheduled-event test mode on a fixed port, 11498, which is how you exercise a cron trigger without waiting for it. The same port appears in the example environment file as the backend port, so the client in development talks to that port while the frontend itself is served from the default local development port of 5173. Two ports, one for the server and one for the site, and a third convention where a periodic check runs against the server port. Around that, the repository is a four-workspace layout: shared packages, the command line wrapper that most root scripts call, the server and the client, orchestrated by a task runner configured at the root and pinned to one specific version of the JavaScript runtime.

## Conclusion

Rin fits someone who wants a personal blog with comments, tags and image hosting and is already on Cloudflare, since the deployment story is the strongest part of the design. Three things to change before you deploy it. The administrator credentials, because the example environment file ships a default administrator username and password and the first account to register takes the administrator role, so anyone who reaches your instance first owns it. The signing secret, which is a placeholder in the same file. And the migration timing, because the deploy command runs database migrations as part of deployment, so a deploy is also a schema change on a live database.

## FAQ

### How do I set up Rin locally?

Clone the repository, install dependencies with bun, copy the example environment file to .env.local and edit it, then run bun run dev and open http://localhost:5173. The package manager is pinned in the manifest to a specific bun version and the repository carries a single bun lockfile, with workspaces for the shared packages, the command line wrapper, the server and the client.

### What does the deploy command in Rin actually do?

One command deploys both the frontend and the backend. It requires a Cloudflare API token and account identifier, and accepts optional resource names with defaults of rin-server for the worker, rin-client for Pages and rin for the database. It creates the database if missing, derives storage settings only when a bucket name is set, deploys the backend to Workers, builds and deploys the frontend to Pages, and runs the database migrations.

### How does Rin handle authentication and admin access?

Login is through GitHub OAuth, and the first registered user becomes an administrator while later users join as regular members. The example environment file also allows an administrator username and password for account-based login, which is the only option when OAuth is not configured, and it ships defaults of admin and admin123 with a placeholder signing secret.

### What are the test and build commands in Rin?

Tests run with the runtime's own test runner invoked as bun test, with filtered variants for the client and for the server and a coverage variant. The server build script runs the deployment tool in dry-run mode into an output directory rather than producing a runnable bundle, while the frontend build is an ordinary workspace build. Database migration, client generation and a one-off column fix are three separate scripts.

### How does Rin check the links in a blogroll?

The blogroll feature keeps links to other blogs and the backend checks their availability every twenty minutes, which is the only recurring job described on the page. It is developed locally with a script that runs the worker in scheduled-event test mode on port 11498, the same port the example environment file gives as the backend port, while the frontend is served from port 5173.

## Sources

- [License: MIT](https://github.com/openRin/Rin/blob/main/LICENSE)
- [openRin/Rin on GitHub](https://github.com/openRin/Rin)
- [Project website](https://docs.openrin.org)
- [README](https://github.com/openRin/Rin/blob/main/README.md)
- [Releases](https://github.com/openRin/Rin/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/openrin-rin
