There's no turnkey way to browse the full app locally on a Mac (https://localhost:4200 → local API) because the frontend's expected API port and the only Mac-runnable API path disagree.
Verified specifics
Frontend (dev):libraries/spikersoft-environment/src/lib/environment.development.ts → baseUrl: "https://localhost:33333". pnpm nx serve spikersoft (HTTPS :4200) sends all API calls to localhost:33333 (only /spikersoft/ebooks is proxied). So the frontend is hard-wired to a local API on :33333.
The :33333 mapping is Windows-only:spikersoft-backend/docker-compose.yml's spikersoft-backend (API) service maps 33333:8081, but it's Visual-Studio/Windows-configured — C:\temp\spikersoft-api-local\... volume mounts, docker-compose.override.yml adds ${APPDATA}/Microsoft/UserSecrets, and it needs the spikersoft-api image built.
docker-compose.mac.yml overrides only infra (rabbitmq/seq/redis/redisinsight) — it does not adapt the API service for Mac. So docker compose … up on a Mac brings up infra (mongo cluster/redis/rabbitmq/jaeger/seq — currently 23 containers) but no working API container.
The Mac-native API path (docs/mac-local-stack.md §2, dotnet run + UseTestAuthentication) binds Kestrel to 5290/5291, not 33333. That runbook is ArtPipe-smoke-focused, not full-app-browse-focused.
Net
Frontend wants :33333; the only Mac-runnable API answers on :5291. Right now nothing is on :33333, so https://localhost:4200 can't reach a local API. Relevant because this Mac is the dev + school-demo rig (per the ProArt epic topology).
Fix options (pick one; low effort)
Documented Mac full-app profile (recommended): run the native API with Kestrel__Endpoints__Https__Url=https://localhost:33333 against the already-up infra + pnpm nx serve spikersoft; add it to mac-local-stack.md as a "browse the full app" section. Decide auth: UseTestAuthentication (quick, role-less) vs. point the local API at real ids.spikersoft.com (faithful roles).
Mac-ready API compose: add a spikersoft-backend override in docker-compose.mac.yml with Mac volume paths (no C:\temp/${APPDATA}) + build config, so docker compose … up yields a :33333 API container.
Align ports: point environment.development.ts at :5291 (the native port) — smallest change, but bakes the ArtPipe-runbook port into the app dev config.
Severity: Low (dev-experience; no prod impact). Surfaced live while wiring up a local browse.
## Verified finding
There's no turnkey way to browse the full app locally on a Mac (`https://localhost:4200` → local API) because the frontend's expected API port and the only Mac-runnable API path disagree.
### Verified specifics
- **Frontend (dev):** `libraries/spikersoft-environment/src/lib/environment.development.ts` → `baseUrl: "https://localhost:33333"`. `pnpm nx serve spikersoft` (HTTPS `:4200`) sends all API calls to `localhost:33333` (only `/spikersoft/ebooks` is proxied). So the frontend is hard-wired to a local API on **:33333**.
- **The `:33333` mapping is Windows-only:** `spikersoft-backend/docker-compose.yml`'s `spikersoft-backend` (API) service maps `33333:8081`, but it's Visual-Studio/Windows-configured — `C:\temp\spikersoft-api-local\...` volume mounts, `docker-compose.override.yml` adds `${APPDATA}/Microsoft/UserSecrets`, and it needs the `spikersoft-api` image built.
- **`docker-compose.mac.yml` overrides only infra** (rabbitmq/seq/redis/redisinsight) — it does **not** adapt the API service for Mac. So `docker compose … up` on a Mac brings up infra (mongo cluster/redis/rabbitmq/jaeger/seq — currently 23 containers) but **no working API container**.
- **The Mac-native API path** (`docs/mac-local-stack.md §2`, `dotnet run` + `UseTestAuthentication`) binds Kestrel to **`5290/5291`**, not `33333`. That runbook is ArtPipe-smoke-focused, not full-app-browse-focused.
### Net
Frontend wants `:33333`; the only Mac-runnable API answers on `:5291`. Right now nothing is on `:33333`, so `https://localhost:4200` can't reach a local API. Relevant because this Mac is the **dev + school-demo rig** (per the ProArt epic topology).
## Fix options (pick one; low effort)
1. **Documented Mac full-app profile (recommended):** run the native API with `Kestrel__Endpoints__Https__Url=https://localhost:33333` against the already-up infra + `pnpm nx serve spikersoft`; add it to `mac-local-stack.md` as a "browse the full app" section. Decide auth: `UseTestAuthentication` (quick, role-less) vs. point the local API at real `ids.spikersoft.com` (faithful roles).
2. **Mac-ready API compose:** add a `spikersoft-backend` override in `docker-compose.mac.yml` with Mac volume paths (no `C:\temp`/`${APPDATA}`) + build config, so `docker compose … up` yields a `:33333` API container.
3. **Align ports:** point `environment.development.ts` at `:5291` (the native port) — smallest change, but bakes the ArtPipe-runbook port into the app dev config.
**Severity:** Low (dev-experience; no prod impact). Surfaced live while wiring up a local browse.
✅ Proven working recipe (real Keycloak, native API on :33333)
Stood the full app up locally and browsed it — https://localhost:4200 → local API :33333 → local infra + real ids.spikersoft.com. Recipe (add to mac-local-stack.md as a "browse the full app" section):
1. Infra — already the docker-compose … up set (mongo cluster :27117, redis :6380, rabbitmq :5672, seq :5341, jaeger :4317).
2. API — native dotnet run, bound to :33333, real Keycloak (the key deltas vs §2's ArtPipe recipe):
OmitAuthentication__UseTestAuthentication → the appsettings Keycloak config (server-url: https://ids.spikersoft.com, validate-issuer: false) drives real JWT validation.
Kestrel__Endpoints__Https__Url=https://localhost:33333 (+ Http__Url=http://localhost:33334) — the frontend's environment.development.ts baseUrl.
Cert: .mac-dev/certs/backend-cert.pem / .key.
Redirect the container upload paths to a writable local dir (new finding — otherwise a 500): LessonVideoStagingService's constructor does Directory.CreateDirectory('/app/uploads/lesson-videos'), and Mac's / is read-only → IOException: Read-only file system : '/app' → 500 on /api/Lessons (and anything else injecting it). Override: Videos__Path, Videos__StagingPath, Books__Path, Books__StagingPath, Blogs__Path, Blogs__StagingPath, Media__Path → a writable local base (e.g. .local-api/.localdata/...). (The blogs/ebooks/media services degrade gracefully with a warning; only LessonVideoStagingService throws from its ctor.)
3. Frontend — pnpm nx serve spikersoft --configuration=development (HTTPS :4200; SSL certs already at workspace-root ssl/localhost.*). CORS is fine — the API's Origins already includes https://localhost:4200.
Gotchas confirmed: browser must trust the :33333 dev cert (visit https://localhost:33333/api/status once, or dotnet dev-certs https --trust); the async worker containers aren't part of this (art-generation/code-exec enqueue but don't complete).
Sub-finding worth its own fix:LessonVideoStagingService doing filesystem CreateDirectoryin its constructor is fragile (any read-only/misconfigured path takes down every endpoint that injects it, via DI). Consider lazy dir-creation or graceful-degrade like the sibling staging services. I can file that separately if you want it tracked.
### ✅ Proven working recipe (real Keycloak, native API on :33333)
Stood the full app up locally and browsed it — `https://localhost:4200` → local API `:33333` → local infra + real `ids.spikersoft.com`. Recipe (add to `mac-local-stack.md` as a "browse the full app" section):
**1. Infra** — already the `docker-compose … up` set (mongo cluster :27117, redis :6380, rabbitmq :5672, seq :5341, jaeger :4317).
**2. API — native `dotnet run`, bound to :33333, real Keycloak** (the key deltas vs §2's ArtPipe recipe):
- **Omit** `Authentication__UseTestAuthentication` → the appsettings Keycloak config (`server-url: https://ids.spikersoft.com`, `validate-issuer: false`) drives real JWT validation.
- `Kestrel__Endpoints__Https__Url=https://localhost:33333` (+ `Http__Url=http://localhost:33334`) — the frontend's `environment.development.ts` baseUrl.
- Cert: `.mac-dev/certs/backend-cert.pem` / `.key`.
- **Redirect the container upload paths to a writable local dir** (new finding — otherwise a 500): `LessonVideoStagingService`'s **constructor** does `Directory.CreateDirectory('/app/uploads/lesson-videos')`, and Mac's `/` is read-only → `IOException: Read-only file system : '/app'` → **500 on `/api/Lessons`** (and anything else injecting it). Override:
`Videos__Path`, `Videos__StagingPath`, `Books__Path`, `Books__StagingPath`, `Blogs__Path`, `Blogs__StagingPath`, `Media__Path` → a writable local base (e.g. `.local-api/.localdata/...`). (The blogs/ebooks/media services degrade gracefully with a warning; only `LessonVideoStagingService` throws from its ctor.)
**3. Frontend** — `pnpm nx serve spikersoft --configuration=development` (HTTPS :4200; SSL certs already at workspace-root `ssl/localhost.*`). CORS is fine — the API's `Origins` already includes `https://localhost:4200`.
**Gotchas confirmed:** browser must trust the `:33333` dev cert (visit `https://localhost:33333/api/status` once, or `dotnet dev-certs https --trust`); the async **worker containers** aren't part of this (art-generation/code-exec enqueue but don't complete).
**Sub-finding worth its own fix:** `LessonVideoStagingService` doing filesystem `CreateDirectory` **in its constructor** is fragile (any read-only/misconfigured path takes down every endpoint that injects it, via DI). Consider lazy dir-creation or graceful-degrade like the sibling staging services. I can file that separately if you want it tracked.
The LessonVideoStagingService ctor-does-filesystem-I/O fragility (the 500 on /api/Lessons) is fixed on backend master — spikersoft-backend#103 (merged): removed the redundant Directory.CreateDirectory from the constructor (StageAsync already creates it lazily at point-of-use), + a regression test. So the constructor no longer takes down unrelated endpoints when the staging path is unwritable.
Note: the Videos/Books/Blogs/Media path overrides are still part of the recipe above — they're needed for actual uploads to land in a writable local dir — but they're no longer required just to keep /api/Lessons from 500'ing. #421 stays open for the turnkey Mac full-app-browse doc/config itself.
### Sub-finding fixed + merged
The `LessonVideoStagingService` ctor-does-filesystem-I/O fragility (the 500 on `/api/Lessons`) is fixed on backend master — **spikersoft-backend#103** (merged): removed the redundant `Directory.CreateDirectory` from the constructor (`StageAsync` already creates it lazily at point-of-use), + a regression test. So the constructor no longer takes down unrelated endpoints when the staging path is unwritable.
Note: the Videos/Books/Blogs/Media **path overrides** are still part of the recipe above — they're needed for actual uploads to land in a writable local dir — but they're no longer required just to keep `/api/Lessons` from 500'ing. #421 stays open for the turnkey Mac full-app-browse doc/config itself.
Resolved in spikersoft-backend PR #209 (merged to master), implementing the ticket's recommended fix option 1: API_HTTP_PORT/API_HTTPS_PORT in scripts/mac-dev-common.sh are now env-overridable (defaults 5290/5291 unchanged), and docs/mac-local-stack.md §2b documents the browse profile — API_HTTPS_PORT=33333 API_HTTP_PORT=33332 scripts/mac-dev-up.sh, then pnpm nx serve spikersoft and browse https://localhost:4200. The open auth question resolved itself via #464's dual-scheme registration: token-less requests get the role-less test-user-id, and a real ids.spikersoft.com SPA login sends a Bearer whose realm roles apply — so anonymous and real-role browsing both work against the local API. Closing.
Resolved in spikersoft-backend PR #209 (merged to master), implementing the ticket's recommended fix option 1: API_HTTP_PORT/API_HTTPS_PORT in scripts/mac-dev-common.sh are now env-overridable (defaults 5290/5291 unchanged), and docs/mac-local-stack.md §2b documents the browse profile — API_HTTPS_PORT=33333 API_HTTP_PORT=33332 scripts/mac-dev-up.sh, then pnpm nx serve spikersoft and browse https://localhost:4200. The open auth question resolved itself via #464's dual-scheme registration: token-less requests get the role-less test-user-id, and a real ids.spikersoft.com SPA login sends a Bearer whose realm roles apply — so anonymous and real-role browsing both work against the local API. Closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Verified finding
There's no turnkey way to browse the full app locally on a Mac (
https://localhost:4200→ local API) because the frontend's expected API port and the only Mac-runnable API path disagree.Verified specifics
libraries/spikersoft-environment/src/lib/environment.development.ts→baseUrl: "https://localhost:33333".pnpm nx serve spikersoft(HTTPS:4200) sends all API calls tolocalhost:33333(only/spikersoft/ebooksis proxied). So the frontend is hard-wired to a local API on :33333.:33333mapping is Windows-only:spikersoft-backend/docker-compose.yml'sspikersoft-backend(API) service maps33333:8081, but it's Visual-Studio/Windows-configured —C:\temp\spikersoft-api-local\...volume mounts,docker-compose.override.ymladds${APPDATA}/Microsoft/UserSecrets, and it needs thespikersoft-apiimage built.docker-compose.mac.ymloverrides only infra (rabbitmq/seq/redis/redisinsight) — it does not adapt the API service for Mac. Sodocker compose … upon a Mac brings up infra (mongo cluster/redis/rabbitmq/jaeger/seq — currently 23 containers) but no working API container.docs/mac-local-stack.md §2,dotnet run+UseTestAuthentication) binds Kestrel to5290/5291, not33333. That runbook is ArtPipe-smoke-focused, not full-app-browse-focused.Net
Frontend wants
:33333; the only Mac-runnable API answers on:5291. Right now nothing is on:33333, sohttps://localhost:4200can't reach a local API. Relevant because this Mac is the dev + school-demo rig (per the ProArt epic topology).Fix options (pick one; low effort)
Kestrel__Endpoints__Https__Url=https://localhost:33333against the already-up infra +pnpm nx serve spikersoft; add it tomac-local-stack.mdas a "browse the full app" section. Decide auth:UseTestAuthentication(quick, role-less) vs. point the local API at realids.spikersoft.com(faithful roles).spikersoft-backendoverride indocker-compose.mac.ymlwith Mac volume paths (noC:\temp/${APPDATA}) + build config, sodocker compose … upyields a:33333API container.environment.development.tsat:5291(the native port) — smallest change, but bakes the ArtPipe-runbook port into the app dev config.Severity: Low (dev-experience; no prod impact). Surfaced live while wiring up a local browse.
✅ Proven working recipe (real Keycloak, native API on :33333)
Stood the full app up locally and browsed it —
https://localhost:4200→ local API:33333→ local infra + realids.spikersoft.com. Recipe (add tomac-local-stack.mdas a "browse the full app" section):1. Infra — already the
docker-compose … upset (mongo cluster :27117, redis :6380, rabbitmq :5672, seq :5341, jaeger :4317).2. API — native
dotnet run, bound to :33333, real Keycloak (the key deltas vs §2's ArtPipe recipe):Authentication__UseTestAuthentication→ the appsettings Keycloak config (server-url: https://ids.spikersoft.com,validate-issuer: false) drives real JWT validation.Kestrel__Endpoints__Https__Url=https://localhost:33333(+Http__Url=http://localhost:33334) — the frontend'senvironment.development.tsbaseUrl..mac-dev/certs/backend-cert.pem/.key.LessonVideoStagingService's constructor doesDirectory.CreateDirectory('/app/uploads/lesson-videos'), and Mac's/is read-only →IOException: Read-only file system : '/app'→ 500 on/api/Lessons(and anything else injecting it). Override:Videos__Path,Videos__StagingPath,Books__Path,Books__StagingPath,Blogs__Path,Blogs__StagingPath,Media__Path→ a writable local base (e.g..local-api/.localdata/...). (The blogs/ebooks/media services degrade gracefully with a warning; onlyLessonVideoStagingServicethrows from its ctor.)3. Frontend —
pnpm nx serve spikersoft --configuration=development(HTTPS :4200; SSL certs already at workspace-rootssl/localhost.*). CORS is fine — the API'sOriginsalready includeshttps://localhost:4200.Gotchas confirmed: browser must trust the
:33333dev cert (visithttps://localhost:33333/api/statusonce, ordotnet dev-certs https --trust); the async worker containers aren't part of this (art-generation/code-exec enqueue but don't complete).Sub-finding worth its own fix:
LessonVideoStagingServicedoing filesystemCreateDirectoryin its constructor is fragile (any read-only/misconfigured path takes down every endpoint that injects it, via DI). Consider lazy dir-creation or graceful-degrade like the sibling staging services. I can file that separately if you want it tracked.Sub-finding fixed + merged
The
LessonVideoStagingServicector-does-filesystem-I/O fragility (the 500 on/api/Lessons) is fixed on backend master — spikersoft-backend#103 (merged): removed the redundantDirectory.CreateDirectoryfrom the constructor (StageAsyncalready creates it lazily at point-of-use), + a regression test. So the constructor no longer takes down unrelated endpoints when the staging path is unwritable.Note: the Videos/Books/Blogs/Media path overrides are still part of the recipe above — they're needed for actual uploads to land in a writable local dir — but they're no longer required just to keep
/api/Lessonsfrom 500'ing. #421 stays open for the turnkey Mac full-app-browse doc/config itself.Resolved in spikersoft-backend PR #209 (merged to master), implementing the ticket's recommended fix option 1: API_HTTP_PORT/API_HTTPS_PORT in scripts/mac-dev-common.sh are now env-overridable (defaults 5290/5291 unchanged), and docs/mac-local-stack.md §2b documents the browse profile — API_HTTPS_PORT=33333 API_HTTP_PORT=33332 scripts/mac-dev-up.sh, then pnpm nx serve spikersoft and browse https://localhost:4200. The open auth question resolved itself via #464's dual-scheme registration: token-less requests get the role-less test-user-id, and a real ids.spikersoft.com SPA login sends a Bearer whose realm roles apply — so anonymous and real-role browsing both work against the local API. Closing.