SpaceWorks 0.8.0 - #16
Merged
Merged
Conversation
Shaan-Shoukath
commented
Sep 1, 2026
Collaborator
- fix(scripts): record the executable bit in git so the tarball ships it
- fix(requests): let public request submission work with the membership module off
- refactor(makerspaces): move the encrypted-secret accessors to a mixin
- feat(makerspaces): anonymous-request principal and its credential refusals
- feat(requests): opt-in account-less public borrow requests
- feat: outbound-only Telegram, account-less requests, staff dock, guard fixes
- fix(requests): make account-less borrowing usable, visible and audited
This repository has core.fileMode = false, so chmod +x never reaches a commit. The mode guard measured os.access() on the working tree, which reports a local bit no clone and no release tarball ever sees. That blind spot voided a production fix. scripts/restore.sh was chmod'd 0755 and guarded on 2026-08-25, the guard went green, and `git archive HEAD` still shipped it 0644 - so import-backup.sh:134 still died at `exec .../restore.sh` with "Permission denied", after recording the restore intent and releasing its lock. install.sh, import-backup.sh and verify-release-images.sh shipped 0644 too; only spaceworks-compose.sh was ever fixed, via git update-index. Modes are now set with git update-index --chmod=+x, and the guard reads `git ls-files -s` because for every script install.sh does not chmod at unpack time, git's mode IS the shipped mode. Verified by clearing the bit in the index while the working tree stayed 755: the guard fails, where before it passed. Co-Authored-By: Shaan-Shoukath <shaanshoukath4522@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… module off
setup.sh ships MSPROFILE="recommended", whose extras include member_accounts but
not membership. request_workflow is core and cannot be turned off. But
RequestSubmitView called require_active_member_presence unconditionally, and that
guard hard-requires an active MakerspaceMembership row without ever consulting the
module registry. So a fresh self-host install that keeps the default profile
shipped unable to accept a public borrow request: a makerspace built from
profile_modules('recommended') returned 403 membership_required to an
authenticated user. The cloud and full profiles include membership and were
unaffected, as is any operator who ticked it on in the setup checklist.
Only RequestSubmitView changes. The membership row is the ONLY thing binding a
user to a makerspace in that guard - is_servable, is_authenticated, is_active and
access_status are all global - so relaxing every caller would let any account act
at any membership-off space. Submitting a request is a proposal staff must
accept; the other surfaces move hardware or reserve capacity, and tests assert
they still refuse.
Waivers cannot be enforced with membership off: acceptance is stored on
MakerspaceMembership and accepting one requires an active membership. In that
configuration the flow is public request -> staff accept.
Co-Authored-By: Shaan-Shoukath <shaanshoukath4522@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex GPT-5.6 <noreply@openai.com>
models_makerspace.py was at 292 lines against the repo's ~300 hard ceiling, and the next phase adds fields to Makerspace. CLAUDE.md requires splitting an at-ceiling file in its own commit before adding to it. The ten encrypt-on-set/decrypt-on-get accessors over five credential columns are one repeated shape with no field definitions, so moving them changes no schema: `makemigrations --check` reports no changes. A mixin rather than free functions because every method reads and writes an attribute of the row it is called on. 260 lines now, and nothing outside the mixin touches the ciphertext columns, which keeps API_CLIENT_ENC_KEY the single decryption door. Co-Authored-By: Shaan-Shoukath <shaanshoukath4522@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…usals Substrate for opt-in account-less borrow requests. No endpoint changes yet. HardwareRequest.requester is a non-null PROTECT FK, so an account-less request still needs a User row - but AuditLog.actor is nullable and record(None, ...) is supported, so anonymous actions will record actor=None rather than be attributed to this principal. One User per anonymous request was rejected outright: both FKs are PROTECT, so every such row would be permanently undeletable, making an unauthenticated endpoint an unbounded undeletable-PII growth path. So: one inert principal per makerspace, created lazily under the makerspace select_for_update lock (same order as walk-in creation, for the deadlock its comment describes), with an unusable password, is_active=False and no contact columns. It is identified by a DB-unique OneToOneField, never a username prefix - the invariants pin walkin_/member_ and a backfill that reads them. anonymous_requests_enabled defaults False and is deliberately NOT inferred from the membership module: recommended installs omit that module, so inferring would silently open an unauthenticated write path on every deployment at upgrade. principal_guards refuses the principal for password set/reset, phone and social binding, and access mutation/restore, in shared services that both the REST paths and the Django admin call - a check in one leaves the other open. Without this the principal was convertible into a real account. Four registries had to be fed, one cascading after the next: the Lane D field snapshot, the catalog digest (updated after diffing catalog_schema, which showed exactly the two new fields and no other model), data_export's relational user edges plus classification, and the deployment-global uniqueness policy - the OneToOne creates a global constraint, and the target must create its own principal rather than import an inert source User. Co-Authored-By: Shaan-Shoukath <shaanshoukath4522@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Codex GPT-5.6 <noreply@openai.com>
RequestSubmitView becomes AllowAny plus an in-view rule, so behaviour is unchanged for every deployment that has not opted in: an anonymous POST to a makerspace without anonymous_requests_enabled still returns exactly today's 401. With the flag on, a stranger submits with contact details and gets a public token. Identity: requester points at the makerspace's inert principal, the human's details go in the existing requester_name/contact_email/contact_phone snapshot columns, and the audit row records actor=None rather than attributing the act to the principal. The principal's username is never copied into the snapshot. Authenticated submissions ignore the contact fields entirely, so a signed-in user cannot use them to impersonate someone. This is an unauthenticated write path, so the limits are part of the feature, not hardening to follow. Each accepted payload creates a request, items, an audit row and a notification fan-out, and a supplied address would otherwise trigger both requester and staff mail - an email-bomb and staff-amplification surface. Hence 2/min and 10/hour per IP, 3/day per email keyed on a fingerprint rather than the address, a ceiling on outstanding anonymous requests, a required Idempotency-Key, and caps on item count, quantity and every text field. Nothing proves a supplied address belongs to the sender and staff acceptance does not prove it either, so the contact is marked unverified and requester lifecycle mail is suppressed until it is. Verified on the running stack, not only in tests: requester is the principal, the principal is inactive with an unusable password, the contact snapshot is correct, no username leaks into the snapshot, and the audit actor is None. Co-Authored-By: Shaan-Shoukath <shaanshoukath4522@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Codex GPT-5.6 <noreply@openai.com>
…d fixes Co-Authored-By: Shaan-Shoukath <shaanshoukath4522@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Shaan-Shoukath <shaanshoukath4522@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Shaan-Shoukath
added a commit
that referenced
this pull request
Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.