π Pre-flight Checks
π Bug Description
When a project is missing from the server's ENGRAM_CLOUD_ALLOWED_PROJECTS, the cloud rejects the push with 403 forbidden: project is not allowed. The client classifies the error correctly but then surfaces a raw transport string with no hint about what to do, and the server never names which project was denied.
The classification itself is fine β internal/cloud/autosync/manager.go:434-452 maps it to policy_forbidden. The problem is everything downstream:
internal/cloud/syncguidance/guidance.go:70 returns the message unchanged unless IsRepairableCloudSyncError(err) is true, and a 403 is not in that set (guidance.go:28-60). So no guidance is ever appended.
cmd/engram/cloud.go:703-729 (printCloudStatusSyncDiagnostic) prints reason_code: / reason_message: verbatim. The only Hint: in that file is line 692, for a missing token.
- Server side,
internal/cloud/cloudserver/mutations.go:255 and cloudserver.go:627,637 return the bare string forbidden: project is not allowed without naming the project or the allowlist variable.
The result is an error that tells you something is forbidden but not what, not why, and not how to fix it. Neither ENGRAM_CLOUD_ALLOWED_PROJECTS nor engram cloud enroll <project> is mentioned anywhere in the output.
π Steps to Reproduce
- Run a cloud server whose
ENGRAM_CLOUD_ALLOWED_PROJECTS omits at least one project that exists locally.
- Enroll that project on the client:
engram cloud enroll <project> β this succeeds, because enrollment is client-side only.
engram cloud upgrade bootstrap --project <project>
β
Expected Behavior
The client should say which project was denied and point at the fix, e.g.:
engram: bootstrap first push: cloud: fetch manifest: status 403: forbidden: project "acme-crm" is not allowed
Hint: the cloud server's ENGRAM_CLOUD_ALLOWED_PROJECTS does not include "acme-crm".
Add it to /etc/engram/engram.env on the server and restart the service.
And engram cloud status should render the same hint alongside reason_code: policy_forbidden.
β Actual Behavior
engram: bootstrap first push: cloud: fetch manifest: status 403: forbidden: project is not allowed
That is the whole output. Nothing indicates the allowlist is the mechanism, nor which project tripped it. With several projects to bootstrap, you cannot tell from the error which ones are affected β you have to bisect by running them one at a time.
The client-side enroll succeeding beforehand actively misleads: it looks like the project is authorised.
Operating System
Linux (Ubuntu/Debian)
Engram Version
1.20.0
Agent / Client
Claude Code
π Relevant Logs
$ engram cloud enroll acme-crm
β Project "acme-crm" enrolled for cloud sync
$ engram cloud upgrade bootstrap --project acme-crm
engram: bootstrap first push: cloud: fetch manifest: status 403: forbidden: project is not allowed
π‘ Additional Context
Suggested fix, small and contained:
- Add a
policy_forbidden branch to the guidance path (internal/cloud/syncguidance), or a Hint: line in cmd/engram/cloud.go:720-728, naming the project and ENGRAM_CLOUD_ALLOWED_PROJECTS.
- Include the denied project name in the server's 403 body (
internal/cloud/cloudserver/mutations.go:255).
Happy to open a PR for this once it has status:approved β it looks like a clean, low-risk change.
Context on how this was hit: while repairing a broken cloud sync, four projects out of twenty-three failed bootstrap with this error and there was no way to tell from the message that a server-side allowlist was involved. Reading the server config was the only way to find out.
π Pre-flight Checks
status:approvedbefore a PR can be openedπ Bug Description
When a project is missing from the server's
ENGRAM_CLOUD_ALLOWED_PROJECTS, the cloud rejects the push with403 forbidden: project is not allowed. The client classifies the error correctly but then surfaces a raw transport string with no hint about what to do, and the server never names which project was denied.The classification itself is fine β
internal/cloud/autosync/manager.go:434-452maps it topolicy_forbidden. The problem is everything downstream:internal/cloud/syncguidance/guidance.go:70returns the message unchanged unlessIsRepairableCloudSyncError(err)is true, and a 403 is not in that set (guidance.go:28-60). So no guidance is ever appended.cmd/engram/cloud.go:703-729(printCloudStatusSyncDiagnostic) printsreason_code:/reason_message:verbatim. The onlyHint:in that file is line 692, for a missing token.internal/cloud/cloudserver/mutations.go:255andcloudserver.go:627,637return the bare stringforbidden: project is not allowedwithout naming the project or the allowlist variable.The result is an error that tells you something is forbidden but not what, not why, and not how to fix it. Neither
ENGRAM_CLOUD_ALLOWED_PROJECTSnorengram cloud enroll <project>is mentioned anywhere in the output.π Steps to Reproduce
ENGRAM_CLOUD_ALLOWED_PROJECTSomits at least one project that exists locally.engram cloud enroll <project>β this succeeds, because enrollment is client-side only.engram cloud upgrade bootstrap --project <project>β Expected Behavior
The client should say which project was denied and point at the fix, e.g.:
And
engram cloud statusshould render the same hint alongsidereason_code: policy_forbidden.β Actual Behavior
That is the whole output. Nothing indicates the allowlist is the mechanism, nor which project tripped it. With several projects to bootstrap, you cannot tell from the error which ones are affected β you have to bisect by running them one at a time.
The client-side
enrollsucceeding beforehand actively misleads: it looks like the project is authorised.Operating System
Linux (Ubuntu/Debian)
Engram Version
1.20.0
Agent / Client
Claude Code
π Relevant Logs
π‘ Additional Context
Suggested fix, small and contained:
policy_forbiddenbranch to the guidance path (internal/cloud/syncguidance), or aHint:line incmd/engram/cloud.go:720-728, naming the project andENGRAM_CLOUD_ALLOWED_PROJECTS.internal/cloud/cloudserver/mutations.go:255).Happy to open a PR for this once it has
status:approvedβ it looks like a clean, low-risk change.Context on how this was hit: while repairing a broken cloud sync, four projects out of twenty-three failed bootstrap with this error and there was no way to tell from the message that a server-side allowlist was involved. Reading the server config was the only way to find out.