aravind changes
This commit is contained in:
94
README.md
94
README.md
@@ -4,13 +4,22 @@ The backend for Krow — a Go API over PostgreSQL, with a Python Owliver service
|
||||
to follow. The Krow frontend lives in a **separate repository** (`krow-demo`)
|
||||
and is not vendored, copied or modified here.
|
||||
|
||||
**Status: Phase 3C — session authentication.** The API serves the endpoints in
|
||||
`docs/api-contract.md` against PostgreSQL, loaded with the frontend's own demo
|
||||
dataset. Every endpoint except `GET /health` and the two `/api/v1/auth/*` routes
|
||||
requires a session: sign in with a password, hold an HttpOnly cookie, and the
|
||||
server resolves it to a real user on every request. Authorization — which roles
|
||||
may do what — is Phase 3D and is not implemented. No Owliver, no RAG, no Redis,
|
||||
NATS or S3.
|
||||
**Status: authenticated, authorized, multi-tenant.** The API serves the
|
||||
endpoints in `docs/api-contract.md` against PostgreSQL, loaded with the
|
||||
frontend's own demo dataset. Every endpoint except `GET /health` and the two
|
||||
`/api/v1/auth/*` routes requires a session: sign in with a password, hold an
|
||||
HttpOnly cookie, and the server resolves it to a real user on every request.
|
||||
Roles are enforced — a deny-by-default policy table answers 403 before any query
|
||||
runs, and organization scope and talent ownership are SQL predicates, so a row
|
||||
you may not see answers 404. On top of that sits the agent and skill definition
|
||||
surface: Markdown with YAML frontmatter, parsed and stored, with full CRUD.
|
||||
|
||||
Not built: no Owliver, no RAG, no Redis, no object storage. The agent/skill
|
||||
runtime is an internal boundary with no executor behind it and no HTTP route
|
||||
into it.
|
||||
|
||||
`docs/KROW_BACKEND_COMPLETE_SUMMARY.md` is the long-form technical account —
|
||||
architecture, history, and a frank list of the current limitations.
|
||||
|
||||
## Layout
|
||||
|
||||
@@ -19,27 +28,39 @@ krow-backend/
|
||||
├── go-api/
|
||||
│ ├── cmd/
|
||||
│ │ ├── api/ the HTTP service entrypoint
|
||||
│ │ └── seed/ loads the demo dataset
|
||||
│ │ ├── seed/ loads the demo dataset
|
||||
│ │ └── setpassword/ the only way a password enters the database
|
||||
│ ├── internal/
|
||||
│ │ ├── config/ environment loading + validation
|
||||
│ │ ├── db/ pgx pool and the database health check
|
||||
│ │ ├── domain/ resource descriptors (generated from the schema)
|
||||
│ │ ├── domain/ resource descriptors (generated) + the policy table
|
||||
│ │ ├── repo/ pgx access layer — every statement built here
|
||||
│ │ ├── service/ validation, org scoping, contract semantics
|
||||
│ │ ├── auth/ argon2id passwords, sessions, the user store
|
||||
│ │ ├── authctx/ the authenticated identity a request carries
|
||||
│ │ ├── orgctx/ the organization a request runs as
|
||||
│ │ ├── definition/ the agent/skill Markdown + YAML frontmatter parser
|
||||
│ │ ├── runtime/ agent/skill load, validate, resolve, dispatch
|
||||
│ │ ├── httpserver/ router, handlers, /health, graceful shutdown
|
||||
│ │ ├── seeder/ fixture loading + the attendanceSeed.js port
|
||||
│ │ └── testutil/ disposable migrated + seeded test database
|
||||
│ └── go.mod
|
||||
├── migrations/ SQL migrations — the source of truth for the schema
|
||||
├── seed/fixtures/ seed.json, generated from the frontend repository
|
||||
├── docs/api-contract.md the frozen client contract
|
||||
├── docs/
|
||||
│ ├── api-contract.md the frozen client contract
|
||||
│ └── KROW_BACKEND_COMPLETE_SUMMARY.md the long-form technical account
|
||||
├── infrastructure/ deployment definitions (empty until later)
|
||||
├── scripts/ verify_schema.sql, gen_resources.py
|
||||
├── scripts/ verify_schema.sql, gen_resources.py, oracle.mjs
|
||||
├── Makefile developer, migration and seed tasks
|
||||
└── .env.example
|
||||
```
|
||||
|
||||
`migrations/`, `seed/`, `scripts/` and `infrastructure/` sit beside `go-api/`
|
||||
rather than inside it because none of them is Go: the migrations are run by the
|
||||
`golang-migrate` CLI, the generator is Python, the parser oracle is Node, and a
|
||||
second service (Owliver, Python) is expected to consume the same schema.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
@@ -82,10 +103,16 @@ make run # http://127.0.0.1:8080/health
|
||||
|
||||
## The API
|
||||
|
||||
38 endpoints, specified in `docs/api-contract.md`. Every one exists because a
|
||||
frontend call site exists — a table in the database is never a reason for an
|
||||
endpoint. `DELETE /job-postings/{id}` and `POST /shift-records` return 405
|
||||
because nothing in the frontend deletes a posting or creates a shift record.
|
||||
54 registered routes: 34 entity routes across 14 resources, 10 agent and skill
|
||||
definition routes, 4 `/me` routes, 2 `/auth` routes, 2 workflow routes, the
|
||||
Owliver suggestion route and `GET /health`. The entity routes and the suggestion
|
||||
route are specified in `docs/api-contract.md` (§2 and §2A); the definition and
|
||||
workflow routes are not yet in the contract and are documented in the source and
|
||||
its tests. Every
|
||||
entity route exists because a frontend call site exists — a table in the
|
||||
database is never a reason for an endpoint. `DELETE /job-postings/{id}` and
|
||||
`POST /shift-records` return 405 because nothing in the frontend deletes a
|
||||
posting or creates a shift record.
|
||||
|
||||
```bash
|
||||
curl localhost:8080/api/v1/job-postings
|
||||
@@ -110,8 +137,19 @@ Set a password before signing in: the seeded user's `password_hash` is NULL unti
|
||||
curl -b jar localhost:8080/api/v1/me
|
||||
curl -b jar -X POST localhost:8080/api/v1/auth/logout
|
||||
|
||||
Authorization is **not** implemented. `users.role` is carried on the identity and
|
||||
consulted nowhere: any signed-in user reaches every endpoint. That is Phase 3D.
|
||||
**Authorization is enforced.** `users.role` — `admin`, `employer` or `talent`,
|
||||
never the self-editable `account_type` — is the only authority. Entity routes
|
||||
are gated by the deny-by-default policy table in `internal/domain/policy.go`:
|
||||
a resource with no policy permits nothing to anyone, and `Server.authorize`
|
||||
answers 403 before any query runs. Row visibility is a SQL predicate rather than
|
||||
a filter — the organization scope always, plus an ownership clause for talent
|
||||
callers — so a row outside it is never fetched and answers 404, which does not
|
||||
distinguish "exists but not yours" from "does not exist".
|
||||
|
||||
The ten definition routes are the exception: they are not `domain.Resource`
|
||||
values, so the descriptor machinery does not reach them and their role checks
|
||||
are written inline in `internal/service/definitions.go`. Tenancy and ownership
|
||||
*are* enforced for them, in the repository predicates.
|
||||
|
||||
## Seeding
|
||||
|
||||
@@ -135,16 +173,30 @@ records created through the API survive a re-seed.
|
||||
make test # go test ./...
|
||||
```
|
||||
|
||||
55 tests. The database-backed ones build a disposable database — dropped,
|
||||
recreated, migrated and seeded per run, named `krow_backend_autotest_<pid>` so
|
||||
concurrent test packages cannot collide. They skip rather than fail when
|
||||
PostgreSQL is unreachable.
|
||||
175 test functions. The database-backed ones build a disposable database —
|
||||
dropped, recreated, migrated and seeded per run, named
|
||||
`krow_backend_autotest_<pid>` so concurrent test packages cannot collide. They
|
||||
skip rather than fail when PostgreSQL is unreachable.
|
||||
|
||||
Seed assertions compare the database against the fixture field-by-field rather
|
||||
than against numbers typed into a test. The two ordering guarantees
|
||||
(`NULLS LAST` in both directions, and the `, id` tiebreaker) are covered by tests
|
||||
verified to fail when the guarantee is removed.
|
||||
|
||||
**Parser conformance.** The Go definition parser must agree with the frontend's
|
||||
JavaScript one. `internal/definition/conformance_test.go` asserts against
|
||||
`internal/definition/testdata/oracle.json`, which is not written by hand: it is
|
||||
captured by running the real frontend module graph through Vite, so the fixture
|
||||
records what the JS parser actually does rather than what anyone believes it
|
||||
does. Regenerating it needs a `krow-demo` checkout, and is not part of
|
||||
`make test`:
|
||||
|
||||
```bash
|
||||
node scripts/oracle.mjs go-api/internal/definition/testdata/oracle.json
|
||||
```
|
||||
|
||||
`scripts/cases.mjs` holds the adversarial corpus that file is captured over.
|
||||
|
||||
`GET /health` is public and says only whether traffic should be sent here:
|
||||
`{"status": "ok"}` with `200`, `{"status": "degraded"}` with `200` when the
|
||||
database is up but unmigrated or left dirty, and `{"status": "unavailable"}`
|
||||
|
||||
Reference in New Issue
Block a user