# hotheadhacker/no-as-a-service: a random rejection API you can self-host

> No-as-a-Service is a small Express app that returns a random rejection reason from reasons.json. It is easy to run locally, but it is a toy API, not a decision engine.

**hotheadhacker/no-as-a-service** — No-as-a-Service (NaaS) is a simple API that returns a random rejection reason. Use it when you need a realistic excuse, a fun “no,” or want to simulate being turned down in style.

- Repository: https://github.com/hotheadhacker/no-as-a-service
- Website: https://naas.isalman.dev/no
- Stars: 7,992 · Forks: 497
- Language: JavaScript
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/hotheadhacker-no-as-a-service

## What no-as-a-service actually returns

The project solves a narrow problem: producing a plausible or funny refusal on demand. The README frames it as a way to say no gracefully, and lists personal, professional, student and developer scenarios. The hosted endpoint is GET https://naas.isalman.dev/no, and the example response contains a single key:

```json
{
  "reason": "This feels like something Future Me would yell at Present Me for agreeing to."
}
```

The audience is therefore developers wiring a joke or a placeholder into a chat bot, a landing page or a demo. It is not an authorization service. Nothing in the repository evaluates a request, a user or a policy. The answer is drawn from reasons.json, which the README describes as holding 1000+ universal rejection reasons. If you need a machine-readable rejection code, a retry hint or a reason category, this API does not provide one; the response shape is a single string field.

## The Express and reasons.json mechanism

The repository layout is small enough to read in one sitting: index.js, reasons.json, package.json, a Dockerfile, .devcontainer.json and an assets directory. package.json names express and express-rate-limit as dependencies, and the repository copy also lists cors, which the README's package.json excerpt omits. That discrepancy is worth knowing before you copy either file: the README block is a reference, not necessarily the current dependency set.

The likely data flow, based on the file names and the Express dependency, is that index.js loads reasons.json into memory at startup and picks one entry per request. Because the reasons live in a JSON file rather than a database, there is no write path, no state and no per-user history. The README states a rate limit of 120 requests per minute per IP, which is consistent with express-rate-limit being a dependency. Self-hosted instances inherit that limit only if index.js applies it; the README does not document how to change or disable the threshold.

## Installing no-as-a-service and making a first request

The README gives a three-step self-hosting path. Clone the repository and enter the directory:

```bash
git clone https://github.com/hotheadhacker/no-as-a-service.git
cd no-as-a-service
```

Install dependencies with npm:

```bash
npm install
```

Then start the server. The README says the API will be live at http://localhost:3000/no:

```bash
npm start
```

To move it off port 3000, the README shows a PORT environment variable:

```bash
PORT=5000 npm start
```

With the server running, a request to the /no path should return a JSON object with a reason field, matching the example response above. If you prefer containers, the repository includes a Dockerfile based on node:22-alpine that runs npm install --omit=dev, copies the source, switches to the node user and exposes port 3000. The README does not give a docker build or docker run command, so the image tag and run flags are up to you.

## Where the project is thin

The README does not document rollback, versioning of the reasons list, or how to add your own entries. There are no retrieved releases, so there is no changelog to consult when behaviour changes. The last push to the repository was on 2026-05-09, which is more than four months before today; the repository is not archived, but the README does not describe a release cadence either.

The response schema is the bigger constraint. One field means a caller cannot distinguish a witty refusal from a temporary failure without inspecting the HTTP status, and the README does not list status codes beyond the example. There is also no locale handling: reasons.json is described as universal, and the README lists a Portuguese-language reimplementation as a separate project, which suggests localization is not built in. For a production decision path, that is the wrong tool entirely.

## How it compares with a policy engine

A real alternative is Open Policy Agent, or an equivalent rules engine, where the refusal is derived from a policy you wrote and the response can carry structured fields. The difference in approach is fundamental. no-as-a-service samples a static list; OPA evaluates input against rules and returns a decision object you can act on. If your bot needs to explain why a request was denied and log that decision, the random list gives you a sentence with no provenance. If your goal is a demo, a Slack command or a Raycast extension, the static list is faster to wire up and has no policy language to learn. The README itself points to community integrations including a Rust implementation, an ASP.NET implementation, a Slack app, a Signal bot, a GNOME search provider and an MCP server, which shows the intended surface is humour and convenience rather than enforcement.

## Licence and upgrade cost

The repository ships an MIT licence, and package.json declares "license": "MIT". That permits reuse and modification, including of reasons.json, provided the licence terms are met. This is a description of the repository metadata, not legal advice; read the LICENSE file and decide with your own counsel if you plan to redistribute a modified reasons list or bundle the API into a commercial product.

Upgrade cost is low but not zero. There is no published release list, so tracking changes means watching commits rather than versions. Dependencies are express, express-rate-limit and cors, all with caret ranges in package.json, which means a fresh npm install can pull newer minor versions than the author tested. A lockfile is not among the top-level entries listed, so pinning is something you would have to add. The Dockerfile installs with npm install --omit=dev, which has the same property. If reproducible builds matter, that is the first thing to fix.

## Conclusion

Adopt no-as-a-service if you want a humorous /no endpoint for a bot, demo or landing page and you are comfortable serving a random string from reasons.json. Do not adopt it if you need structured rejection codes, audit trails or any logic behind the answer, because the API returns exactly one field and no reason metadata. Before you deploy, confirm how you will pin the image or lockfile, and check the MIT licence text in LICENSE if you plan to redistribute a modified reasons.json.

## FAQ

### What does no-as-a-service do?

It is a small API that returns a random rejection reason. The README describes it as a way to get a realistic excuse, a fun no, or a simulated rejection, and the response contains a single reason field.

### What does NaaS stand for in this project?

The README and the repository name use NaaS as shorthand for no-as-a-service. Note that the same acronym is used elsewhere for network-as-a-service, which is a different subject.

### How do I self-host no-as-a-service?

Clone the repository, run npm install, then npm start. The README says the API will be live at http://localhost:3000/no, and you can change the port with the PORT environment variable.

### Is there a rate limit on the no-as-a-service API?

The README states a limit of 120 requests per minute per IP. The package.json lists express-rate-limit as a dependency, but the README does not document how to change that threshold.

### Can I add my own rejection reasons to no-as-a-service?

The reasons live in reasons.json, which the README describes as holding 1000+ universal rejection reasons. The README does not document a supported way to extend or version that file.

## Sources

- [hotheadhacker/no-as-a-service on GitHub](https://github.com/hotheadhacker/no-as-a-service)
- [Issues](https://github.com/hotheadhacker/no-as-a-service/issues)
- [License: MIT](https://github.com/hotheadhacker/no-as-a-service/blob/main/LICENSE)
- [Project website](https://naas.isalman.dev/no)
- [README](https://github.com/hotheadhacker/no-as-a-service/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/hotheadhacker-no-as-a-service
