- GET /_bm/api/v1/tags: the jz_bm_tags dictionary, to material managers
- POST /materials/{id}/unpublish: back to drafts, no revision bump, the
search document resynced
- POST /materials/{id}/images: one JPEG/PNG/GIF/WebP per request through
storeMaterialUpload, refusals with reason type, size or count
- storeMaterialUpload/fileRefusalFor: attach.ErrNotImage is a type
refusal, not a 500; storeMaterialFile keeps its texts
- the author's routes get their own per-user buckets (the inline
throttle form shares one counter per user)
- README: the author routes and revision on the student detail
- POST /materials locks the matched row and answers 409 to a stale
revision when it updates; a create ignores revision
- bm_manage_save_material and bm_manage_update_material take revision
- /bmadmin saves bump the revision on content changes (not checked)
- document revisions and the 409 in the README
The backend materials list shows the zjazd (meeting, by number). The
MCP save and update material tools, the README material example and the
management token test stop modelling a zjazd as a tag, and the README
describes the one-way zjazd tag migration.
- inline and signed uploads queue the text job in their transaction
- the admin form queues it in the save transaction
- detach resyncs the material (its text row goes by cascade)
- bm:materials-reindex backfills missing PDF texts before each sync
- GET /api/v1/bm/materials takes q: candidates over every material, in
rank order, with the snippet of the student list; 503 when the engine
cannot answer
- bm_materials and bm_manage_list_materials take q; under MCP the snippet
is plain text with ellipses where text was cut
- MCPMaterialPrivacy no longer claims no file text is ever returned
- README documents q, the snippet, the 503, the Search section and
bm:materials-reindex
The management save/update body takes is_private, author_user_id and
share_user_ids (absent keeps each; a new material gets the caller as
author), and the managed material shows them. New routes PATCH
/materials/{id}/visibility, PUT /materials/{id}/shares and GET /users,
with the MCP tools bm_manage_set_material_visibility,
bm_manage_set_material_shares and bm_manage_list_users. The /bmadmin
form gets the private switch, the author picker and shared_with.
Per-user checklists with items that may link a course, reading, deadline,
material or own presentation deck. An item with a reading shares the
reading's read mark. Student API routes with token twins, MCP tools,
migrations, tests and README.
- classes/uploads.go: tickets signed with HMAC-SHA256 under the
jz.bm.upload.v1: label (app.key), 10-minute expiry, the nonce pulled
from the cache once (duplicated from golem15.presentations on purpose)
- POST /api/v1/bm/materials/{id}/files/upload-url issues the URL with a
curl example; POST /_bm/api/v1/uploads/{ticket} takes one file part,
no bearer, throttled per IP and per ticket, user and permission
re-checked, stored through storeMaterialFile
- bm_manage_create_material_upload; README documents the file routes
- 28 typed tools over /api/v1/bm: list, show, create and update for
courses and meetings, plus delete for class sessions, readings,
deadlines and materials (save upserts on title and course)
- inputs mirror Fillable and Rules; enums, lengths and date formats in
the field descriptions; no seed, me or dry_run tool
- materials accept meeting_id (absent keeps, null clears, must exist)
and the managed material JSON shows it
- README: MCP tools section
The material admin form gets the "Na zjazd" picker, every student
material JSON carries meeting_id, and GET /materials accepts meeting=
inside the published-only scope, so the Pulpit hero can list the
zjazd's materials next to its readings.
- new table jz_bm_reading_ownership and its migration
20261026120000_create_jz_bm_reading_ownership
- PUT and DELETE /_bm/api/v1/readings/{id}/owned, idempotent
- owned_at on every reading in /readings and /courses/{id}
- read semantics, progress routes and counts unchanged
The management API could only upsert a material on its title and course,
so a renamed note became a second material. PUT /materials/{id} applies the
same body to one material (422 when the new title and course belong to
another one), GET /materials/{id} shows a draft or published material with
its public files, and the upsert, update and validation share one path.
The README documents the management API.
Datepickers on native time.Time / lagoon.Date fields, file uploads on
materials, readings and class sessions managed from the course form
(course_id made nullable).
Overview, features, the API reference with every route and response
shape, admin screens, configuration, the bm:seed file format,
dependencies and testing.