Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
c1ce69f
initial commit for no-auth local mode
mnafees Jul 1, 2026
e0eb1c0
no-auth authz
mnafees Jul 1, 2026
31afe32
CLI and docs
mnafees Jul 1, 2026
62f1aaf
fix build error
mnafees Jul 1, 2026
afeb5fb
error boundary
mnafees Jul 1, 2026
9e16f52
documentation changes
mnafees Jul 1, 2026
ad36f41
local keyset
mnafees Jul 2, 2026
b398a8d
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 2, 2026
3160acb
no auth keyset in hatchet lite + docs
mnafees Jul 2, 2026
1b90b11
rm stale file
mnafees Jul 2, 2026
7b91740
edit doc
mnafees Jul 2, 2026
5ee5b2e
no auth user default to seed admin
mnafees Jul 2, 2026
4a3f9cb
fmt docs
mnafees Jul 2, 2026
8190d2c
simple test for noauth tokens
mnafees Jul 2, 2026
1239b41
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 2, 2026
b574633
auth disabled go build tag
mnafees Jul 3, 2026
a2621cf
dev image
mnafees Jul 3, 2026
f7b85f6
rename strings
mnafees Jul 3, 2026
ee09f00
rename files
mnafees Jul 3, 2026
186511d
update docs
mnafees Jul 3, 2026
2801cf2
prose rewording
mnafees Jul 3, 2026
7e70b77
upload authmode disabled keyset
mnafees Jul 3, 2026
6f35c6a
format
mnafees Jul 5, 2026
955ceca
remove code
mnafees Jul 5, 2026
e84bee1
fix precommitlint
mnafees Jul 5, 2026
ddc7655
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 6, 2026
07fd6b9
fix banner
mnafees Jul 6, 2026
89882df
UI changes for no auth mode
mnafees Jul 6, 2026
be1d730
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 6, 2026
e3da4fe
embed the JWT itself for auth disabled mode
mnafees Jul 6, 2026
898e04b
sane expiry
mnafees Jul 6, 2026
172c9df
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 7, 2026
f558c89
PR comments
mnafees Jul 7, 2026
c0ad591
test for noauth
mnafees Jul 7, 2026
f5dfd4e
auth disabled test
mnafees Jul 7, 2026
a36199c
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 7, 2026
afa0ead
PR comments
mnafees Jul 8, 2026
6db028a
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 8, 2026
f3f18cf
ignore noauth keysets and jwt for gitguardian
mnafees Jul 8, 2026
819c13c
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 8, 2026
305d709
resolve PR comments
mnafees Jul 8, 2026
ed7d836
Merge branch 'main' into nafees/local-no-auth
abelanger5 Jul 8, 2026
54b76a4
chore: make sure we are only building stripped go binaries for Docker…
mnafees Jul 9, 2026
ed9477f
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 9, 2026
07c3852
update docs
mnafees Jul 9, 2026
d5c467e
update docs and build dashboard-dev images as well
mnafees Jul 9, 2026
a327291
Merge branch 'main' into nafees/local-no-auth
mnafees Jul 9, 2026
7e954e8
build all 5 images authdisabled in PR CI
mnafees Jul 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitguardian.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ version: 2
secret:
ignored_paths:
- 'frontend/app/cypress/e2e/**'
- 'pkg/authmode/keyset/**'
27 changes: 27 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,33 @@ jobs:
- name: Build servers
run: docker build -f ./build/package/servers.dockerfile . --build-arg SERVER_TARGET=engine

authdisabled:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Build api (authdisabled)
run: docker build -f ./build/package/servers.dockerfile . --build-arg SERVER_TARGET=api --build-arg GO_BUILD_TAGS=authdisabled
- name: Build engine (authdisabled)
run: docker build -f ./build/package/servers.dockerfile . --build-arg SERVER_TARGET=engine --build-arg GO_BUILD_TAGS=authdisabled
- name: Build admin (authdisabled)
run: docker build -f ./build/package/servers.dockerfile . --build-arg SERVER_TARGET=admin --build-arg GO_BUILD_TAGS=authdisabled
- name: Build lite (authdisabled)
run: docker build -f ./build/package/servers.dockerfile . --build-arg SERVER_TARGET=lite --build-arg GO_BUILD_TAGS=authdisabled
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14'
- name: Generate docs snippets and examples
working-directory: frontend/snippets
run: python3 generate.py
- name: Build dashboard (authdisabled)
run: |
DOCKER_BUILDKIT=1 docker build -f ./build/package/servers.dockerfile \
--build-arg SERVER_TARGET=api --build-arg GO_BUILD_TAGS=authdisabled \
-t hatchet-api-dev-tmp .
DOCKER_BUILDKIT=1 docker build -f ./build/package/dashboard.dockerfile \
--build-arg HATCHET_API_IMAGE=hatchet-api-dev-tmp .

admin:
runs-on: ubuntu-latest
steps:
Expand Down
516 changes: 504 additions & 12 deletions .github/workflows/pre-release.yaml
Comment thread
mnafees marked this conversation as resolved.

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,18 @@ jobs:
ghcr.io/hatchet-dev/hatchet/hatchet-lite:${{steps.tag_name.outputs.tag}}-arm64

docker manifest push ghcr.io/hatchet-dev/hatchet/hatchet-lite:latest
- name: Pull and push dev images
run: |
for img in hatchet-api hatchet-engine hatchet-admin hatchet-lite hatchet-dashboard; do
docker pull ghcr.io/hatchet-dev/hatchet/$img-dev:${{steps.tag_name.outputs.tag}}-arm64
docker pull ghcr.io/hatchet-dev/hatchet/$img-dev:${{steps.tag_name.outputs.tag}}-amd64

docker manifest create ghcr.io/hatchet-dev/hatchet/$img-dev:latest \
ghcr.io/hatchet-dev/hatchet/$img-dev:${{steps.tag_name.outputs.tag}}-amd64 \
ghcr.io/hatchet-dev/hatchet/$img-dev:${{steps.tag_name.outputs.tag}}-arm64

docker manifest push ghcr.io/hatchet-dev/hatchet/$img-dev:latest
done
- name: Pull and push hatchet-dashboard
run: |
docker pull ghcr.io/hatchet-dev/hatchet/hatchet-dashboard:${{steps.tag_name.outputs.tag}}-arm64
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ repos:
exclude: ^examples/
- id: mixed-line-ending
args: ["--fix=lf"]
exclude: ^examples/
exclude: (^examples/|^pkg/authmode/keyset/)
- id: end-of-file-fixer
exclude: ^examples/
exclude: (^examples/|^pkg/authmode/keyset/)
- id: trailing-whitespace
exclude: ^examples/
exclude: (^examples/|^pkg/authmode/keyset/)
- id: check-yaml
exclude: (^examples/|^cmd/hatchet-cli/cli/templates/)
- repo: https://github.com/golangci/golangci-lint
Expand Down
8 changes: 8 additions & 0 deletions api-contracts/openapi/components/schemas/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ APIMeta:
type: boolean
description: whether or not a Prometheus federation server is configured (SERVER_PROMETHEUS_SERVER_URL) on this instance
example: false
authDisabled:
type: boolean
description: whether or not authentication is disabled (authdisabled build) on this instance
example: false
authDisabledToken:
type: string
description: the embedded worker API token, only set on authdisabled builds
example: eyJhbGciOiJFUzI1NiIs...

APIMetaAuth:
type: object
Expand Down
26 changes: 26 additions & 0 deletions api/v1/server/authn/authdisabled.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//go:build authdisabled

package authn

import (
"net/http"

"github.com/labstack/echo/v4"
)

func (a *AuthN) authPreflight(c echo.Context) (handled bool, err error) {
ctx := c.Request().Context()

user, err := a.config.V1.User().GetUserByEmail(ctx, a.config.Seed.AdminEmail)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

open question: should this be the seeded user or should we force an email env var or something?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose to simply reuse the seeded tenant and user to avoid adding in more config options


if err != nil {
a.l.Error().Ctx(ctx).Err(err).Msg("authdisabled: could not resolve the seeded admin user")

return true, echo.NewHTTPError(http.StatusInternalServerError, "authdisabled: could not resolve the seeded admin user; ensure the database was seeded (ADMIN_EMAIL/ADMIN_PASSWORD)")
}

c.Set("user", user)
c.Set("auth_strategy", "authdisabled")

return true, nil
}
9 changes: 9 additions & 0 deletions api/v1/server/authn/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !authdisabled

package authn

import "github.com/labstack/echo/v4"

func (a *AuthN) authPreflight(c echo.Context) (handled bool, err error) {
return false, nil
}
7 changes: 7 additions & 0 deletions api/v1/server/authn/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ func (a *AuthN) authenticate(c echo.Context, r *middleware.RouteInfo) error {
return a.handleNoAuth(c)
}

// authPreflight only returns handled=true in authdisabled builds, where it resolves the
// request as the seeded admin and short-circuits the strategies below. In normal builds it
// is a no-op (returns false), so authentication always proceeds. Do not invert this check.
if handled, err := a.authPreflight(c); handled {
return err
}
Comment thread
mnafees marked this conversation as resolved.

var bearerErr error

if r.Security.BearerAuth() {
Expand Down
17 changes: 17 additions & 0 deletions api/v1/server/authz/authdisabled.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//go:build authdisabled

package authz

import (
"github.com/labstack/echo/v4"

"github.com/hatchet-dev/hatchet/api/v1/server/middleware"
)

func (a *AuthZ) authPreflight(c echo.Context, r *middleware.RouteInfo) (handled bool, err error) {
if err := a.authorizeTenantOperations("NOAUTH", r); err != nil {
return true, err
}

return true, a.validateUserTenantPermissions(c, r)
}
13 changes: 13 additions & 0 deletions api/v1/server/authz/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build !authdisabled

package authz

import (
"github.com/labstack/echo/v4"

"github.com/hatchet-dev/hatchet/api/v1/server/middleware"
)

func (a *AuthZ) authPreflight(c echo.Context, r *middleware.RouteInfo) (handled bool, err error) {
return false, nil
}
13 changes: 10 additions & 3 deletions api/v1/server/authz/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ func (a *AuthZ) authorize(c echo.Context, r *middleware.RouteInfo) error {
return nil
}

// authPreflight only returns handled=true in authdisabled builds, where it authorizes the
// request against the NOAUTH role and short-circuits the strategy switch below. In normal
// builds it is a no-op (returns false), so authorization always proceeds. Do not invert this.
if handled, err := a.authPreflight(c, r); handled {
return err
}
Comment thread
mnafees marked this conversation as resolved.

var err error

switch c.Get("auth_strategy").(string) {
Expand Down Expand Up @@ -182,7 +189,7 @@ func (a *AuthZ) validateUserTenantPermissions(c echo.Context, r *middleware.Rout
c.Set("tenant-member", tenantMember)

// authorize tenant operations
if err := a.authorizeTenantOperations(tenantMember.Role, r); err != nil {
if err := a.authorizeTenantOperations(string(tenantMember.Role), r); err != nil {
a.l.Debug().Ctx(ctx).Err(err).Msgf("error authorizing tenant operations")

return unauthorized
Expand All @@ -192,14 +199,14 @@ func (a *AuthZ) validateUserTenantPermissions(c echo.Context, r *middleware.Rout
return nil
}

func (a *AuthZ) authorizeTenantOperations(tenantMemberRole sqlcv1.TenantMemberRole, r *middleware.RouteInfo) error {
func (a *AuthZ) authorizeTenantOperations(roleName string, r *middleware.RouteInfo) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the reason for this change?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we now have an internal-only NOAUTH role which does not conform to a sqlcv1.TenantMemberRole anymore

// if the operation is in the allowed operations, skip the RBAC check this is needed for extensions
if rbac.OperationIn(r.OperationID, a.config.Auth.AllowedOperations) {
return nil
}

// at the moment, tenant members are only restricted from creating other tenant users.
if !a.rbac.IsAuthorized(string(tenantMemberRole), r.OperationID) {
if !a.rbac.IsAuthorized(roleName, r.OperationID) {
return echo.NewHTTPError(http.StatusUnauthorized, "Not authorized to perform this operation")
}

Expand Down
2 changes: 2 additions & 0 deletions api/v1/server/authz/rbac.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
roles:
OWNER:
inherits: [ADMIN]
NOAUTH:
inherits: [MEMBER]
ADMIN:
inherits: [MEMBER]
permissions:
Expand Down
10 changes: 10 additions & 0 deletions api/v1/server/handlers/metadata/get.go
Comment thread
mnafees marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/labstack/echo/v4"

"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
"github.com/hatchet-dev/hatchet/pkg/authmode"
)

func (u *MetadataService) MetadataGet(ctx echo.Context, request gen.MetadataGetRequestObject) (gen.MetadataGetResponseObject, error) {
Expand Down Expand Up @@ -36,6 +37,8 @@ func (u *MetadataService) MetadataGet(ctx echo.Context, request gen.MetadataGetR

prometheusServerEnabled := u.config.Prometheus.PrometheusServerURL != ""

authDisabled := authmode.IsDisabled

meta := gen.APIMeta{
Auth: &gen.APIMetaAuth{
Schemes: &authTypes,
Expand All @@ -48,6 +51,13 @@ func (u *MetadataService) MetadataGet(ctx echo.Context, request gen.MetadataGetR
AllowChangePassword: &u.config.Runtime.AllowChangePassword,
ObservabilityEnabled: &observabilityEnabled,
PrometheusServerEnabled: &prometheusServerEnabled,
AuthDisabled: &authDisabled,
}

if authDisabled {
if embeddedToken := authmode.EmbeddedToken(); embeddedToken != "" {
meta.AuthDisabledToken = &embeddedToken
}
}

return gen.MetadataGet200JSONResponse(meta), nil
Expand Down
Loading
Loading