# idear

Roles and membership for a [Rastrillo](https://rastrillo.org) app: who is in
this instance, at what rank, and who may change that.

Owner / Admin / Member, exactly one Owner at all times. Invitations that are
single-use, expiring, and stored only as a digest. A membership gate that
answers a non-member and a removed member identically. Removal that is a
deactivation and never a delete, so nothing in your tables dangles.

```sh
go get amadan.net/rastrillo/idear
```

No `replace` directive: idear is fetched by path like any other module.

## What it is not

It does not mint sessions, hash passwords, or render a sign-in form — it
sits on top of `rastrillo/sessions` and whichever identity plugin the app
already chose, `password` or `auth` (keymail). Pick one, not both.

It does not do tenancy. A CARLOS app serves one team per instance;
separating teams is the platform's process-and-file boundary, not a `WHERE`
clause. idear decides who may do what *inside* one instance.

It does not own your pages. Rendering goes through callbacks you supply, the
way `password.Config.RenderSignin` does.

## Where to start

**[`SKILL.md`](SKILL.md)** is the authoring doc — read it instead of the
source. It carries the wiring, the route table, the two identity adapters,
the security discipline, and the traps that are silent when you get them
wrong. Once `go get` has fetched the module, it's already on disk: an
agent can read it straight out of the module cache, pinned to the exact
version the app resolved (no URL to drift from the code it documents):

```sh
cat "$(go list -m -f '{{.Dir}}' amadan.net/rastrillo/idear)/SKILL.md"
```

**[`example/`](example/)** is a complete working app on rastrillo + idear,
and `example/app_test.go` drives the whole flow — sign up, claim, invite,
accept, members page, role change — through real HTTP.
