{"record":{"id":"b5f72b8fe5f4674f","repo":"vxcontrol/pentagi","slug":"token-creationdisabled","errorCode":"Token.CreationDisabled","errorMessage":"token creation is disabled with default salt","messagePattern":"token creation is disabled with default salt","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"critical","filePath":"backend/pkg/server/services/api_tokens.go","lineNumber":61,"sourceCode":"\t\tss:         ss,\n\t}\n}\n\n// CreateToken creates a new API token\n// @Summary Create new API token for automation\n// @Tags Tokens\n// @Accept json\n// @Produce json\n// @Param json body models.CreateAPITokenRequest true \"Token creation request\"\n// @Success 201 {object} response.successResp{data=models.APITokenWithSecret} \"token created successful\"\n// @Failure 400 {object} response.errorResp \"invalid token request or default salt\"\n// @Failure 403 {object} response.errorResp \"creating token not permitted\"\n// @Failure 500 {object} response.errorResp \"internal error on creating token\"\n// @Router /tokens [post]\nfunc (s *TokenService) CreateToken(c *gin.Context) {\n\tif s.globalSalt == \"\" || s.globalSalt == \"salt\" {\n\t\tlogger.FromContext(c).Errorf(\"token creation attempted with default salt\")\n\t\tresponse.Error(c, response.ErrTokenCreationDisabled, errors.New(\"token creation is disabled with default salt\"))\n\t\treturn\n\t}\n\n\tuid := c.GetUint64(\"uid\")\n\trid := c.GetUint64(\"rid\")\n\tuhash := c.GetString(\"uhash\")\n\n\tvar req models.CreateAPITokenRequest\n\tif err := c.ShouldBindJSON(&req); err != nil {\n\t\tlogger.FromContext(c).WithError(err).Errorf(\"error binding JSON\")\n\t\tresponse.Error(c, response.ErrTokenInvalidRequest, err)\n\t\treturn\n\t}\n\tif err := req.Valid(); err != nil {\n\t\tlogger.FromContext(c).WithError(err).Errorf(\"error validating JSON\")\n\t\tresponse.Error(c, response.ErrTokenInvalidRequest, err)\n\t\treturn\n\t}","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/api_tokens.go#L43-L79","documentation":"TokenService.CreateToken refuses to mint API tokens when the server's global salt is empty or still the default value 'salt', because HMAC/token signing with a default salt would let anyone forge tokens. This is a deliberate security kill-switch: token creation stays disabled until a real salt is configured, and the endpoint returns Token.CreationDisabled (403).","triggerScenarios":"POST /tokens while the backend was started without a configured global salt (empty env value) or with the literal placeholder 'salt' (the shipped docker-compose default).","commonSituations":"Fresh deployment where .env was copied but the salt variable never changed; docker-compose defaults left in place; CI/staging environments reusing example configs; operator tried to create a personal API token right after install.","solutions":["Set a strong, unique global salt in the environment (e.g. SALT=<random 32+ byte secret>) and restart the backend.","Replace the default 'salt' value in .env / docker-compose.yml with a generated secret (openssl rand -hex 32).","Confirm the salt is propagated to all backend replicas — a mismatched salt invalidates tokens across instances.","If token creation is intentionally disabled, use an identity provider (OAuth2) instead of API tokens."],"exampleFix":"// before (.env)\nSALT=salt\n// after\nSALT=9f2c7a1e4b8d...generated-64-hex-chars","handlingStrategy":"validation","validationCode":"// ops check before deploying / calling POST /tokens\nif [ -z \"$SALT\" ] || [ \"$SALT\" = \"salt\" ]; then echo \"FATAL: set a strong SALT\"; exit 1; fi","typeGuard":"func tokenCreationEnabled(salt string) bool { return salt != \"\" && salt != \"salt\" }","tryCatchPattern":"try { await api.post('/tokens', body); }\ncatch (e) {\n  if (e.response?.status === 403 && e.response?.data?.code === 'Token.CreationDisabled') {\n    notifyAdmin('set a non-default global salt to enable token creation');\n  } else throw e;\n}","preventionTips":["Generate a unique salt at install time (openssl rand -hex 32).","Never ship 'salt' as a real value in compose/k8s manifests.","Add a startup health check that warns when the default salt is in use.","Document in onboarding that API tokens are disabled until the salt is configured."],"tags":["security","api-tokens","configuration","secrets"],"backgroundTag":"default-salt-token-creation-disabled","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}