Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
19fc7df
feat(guestauth): added guestauth service skeleton
maki5 Sep 22, 2026
0448b45
feat(guestauth): added consumer and some basic event handling logic w…
maki5 Sep 22, 2026
c5e52f9
feat(guestauth): token service
maki5 Sep 22, 2026
06f7d12
feat(guestauth): storage implementation for guest auth tokens
maki5 Sep 23, 2026
a60ec8c
feat(guestauth): handler for redeem action and http service
maki5 Sep 23, 2026
7af24c0
feat(guestauth): handle events and a bit of cleanup
maki5 Sep 24, 2026
ecb546f
feat(guestauth): create jwt token and setup cookies on redeem token call
maki5 Sep 25, 2026
ffa70f5
feat(guestauth): upd events handlers tests
maki5 Sep 25, 2026
2fc5fb3
feat(guestauth): used renamio for file write
maki5 Sep 25, 2026
a1cb335
feat(guestauth): register gusetauth service into opencloud config
maki5 Sep 25, 2026
402a0c0
feat(guestauth): register guestauth proxy
maki5 Sep 25, 2026
6ea563d
feat(guestauth): added mocks for tests, made services dependencies to…
maki5 Sep 25, 2026
45bc705
feat(guestauth): cookie set always to true due to codacy mention
maki5 Sep 28, 2026
d359a7d
feat(guestauth): removed uneeded config field
maki5 Sep 28, 2026
fdf52e4
feat(guestauth): upd service readme
maki5 Sep 28, 2026
2106f60
feat(guestauth): upd version tags for env vars
maki5 Sep 29, 2026
b4aa47c
feat(guestauth): renamed Storage interface and dependencies to Manager
maki5 Sep 29, 2026
e5c3be7
feat(guestauth): moved SecretHash to a method instead of field in struct
maki5 Sep 29, 2026
b920654
feat(guetauth): upd the redeem route
maki5 Sep 29, 2026
9088273
feat(guestauth): upd token record expiry proporety value on add
maki5 Sep 29, 2026
eac68b9
feat(guestauth): upd redeem response error format
maki5 Sep 29, 2026
cf77e47
feat(guestauth): used file lock instead of mutex in file strorage imp…
maki5 Sep 29, 2026
f86e339
feat(guestauth): grantee email in GuestTokenCreated event
maki5 Sep 29, 2026
cbf3ee8
feat(guestauth): guestauth service rename to auth-guest
maki5 Sep 29, 2026
d084709
feat(guestauth): prefix for cookie
maki5 Sep 29, 2026
e17a9a3
feat(guestauth): aligned readme
maki5 Sep 30, 2026
340851d
feat(guestauth): updated redeem endpoint response and dependencies
maki5 Sep 30, 2026
a1a8221
feat(guestauth): global flag to disable the guest links feature
maki5 Oct 1, 2026
c5879ca
feat(guestauth): renaming guest ivitations to guest links for consist…
maki5 Oct 1, 2026
2c89825
feat(guestauth): upd file manager lock, addtional filetrs for shareEx…
maki5 Oct 1, 2026
c1dc12d
feat(guestauth): upd readme with guest links flow diagram
maki5 Oct 1, 2026
aeb11b0
Adapt to renamed env var about guest links
aduffeck Oct 6, 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
2 changes: 1 addition & 1 deletion .woodpecker.star
Original file line number Diff line number Diff line change
Expand Up @@ -2482,6 +2482,7 @@ def opencloudServer(storage = "decomposed", depends_on = [], deploy_type = "", e
"OC_TRANSLATION_PATH": "%s/tests/config/translations" % dirs["base"],
"ACTIVITYLOG_WRITE_BUFFER_DURATION": "0", # Disable write buffer so that test expectations are met in time
"OC_LDAP_LOOKUP_CACHE_TTL": "0", # disable ldap lookup cache so that test fixture change are applied right away
"OC_ENABLE_GUEST_LINKS": True,
# search grpc port needed for index cli tests
"SEARCH_GRPC_ADDR": "0.0.0.0:9220",
# debug addresses required for running services health tests
Expand Down Expand Up @@ -2521,7 +2522,6 @@ def opencloudServer(storage = "decomposed", depends_on = [], deploy_type = "", e
"WEBFINGER_DEBUG_ADDR": "0.0.0.0:9279",
"STORAGE_USERS_POSIX_SCAN_DEBOUNCE_DELAY": 0,
"OC_MACHINE_AUTH_API_KEY": MACHINE_AUTH_API_KEY,
"GRAPH_ENABLE_GUEST_INVITES": True,
}

if storage == "posix":
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ OC_MODULES = \
services/auth-app \
services/auth-basic \
services/auth-bearer \
services/auth-guest \
services/auth-machine \
services/auth-service \
services/clientlog \
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ require (
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.7.0
github.com/google/go-tika v0.3.1
github.com/google/renameio/v2 v2.0.2
github.com/google/uuid v1.6.0
github.com/gookit/config/v2 v2.2.9
github.com/gorilla/mux v1.8.1
Expand Down Expand Up @@ -235,7 +236,6 @@ require (
github.com/google/go-querystring v1.2.0 // indirect
github.com/google/go-tpm v0.9.8 // indirect
github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect
github.com/google/renameio/v2 v2.0.2 // indirect
github.com/gookit/goutil v0.8.0 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/schema v1.4.1 // indirect
Expand Down
6 changes: 6 additions & 0 deletions opencloud/pkg/command/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
authapp "github.com/opencloud-eu/opencloud/services/auth-app/pkg/command"
authbasic "github.com/opencloud-eu/opencloud/services/auth-basic/pkg/command"
authbearer "github.com/opencloud-eu/opencloud/services/auth-bearer/pkg/command"
authguest "github.com/opencloud-eu/opencloud/services/auth-guest/pkg/command"
authmachine "github.com/opencloud-eu/opencloud/services/auth-machine/pkg/command"
authservice "github.com/opencloud-eu/opencloud/services/auth-service/pkg/command"
clientlog "github.com/opencloud-eu/opencloud/services/clientlog/pkg/command"
Expand Down Expand Up @@ -138,6 +139,11 @@ var serviceCommands = []register.Command{
cfg.Groups.Commons = cfg.Commons
})
},
func(cfg *config.Config) *cobra.Command {
return ServiceCommand(cfg, cfg.AuthGuest.Service.Name, authguest.GetCommands(cfg.AuthGuest), func(c *config.Config) {
cfg.AuthGuest.Commons = cfg.Commons
})
},
func(cfg *config.Config) *cobra.Command {
return ServiceCommand(cfg, cfg.IDM.Service.Name, idm.GetCommands(cfg.IDM), func(c *config.Config) {
cfg.IDM.Commons = cfg.Commons
Expand Down
16 changes: 16 additions & 0 deletions opencloud/pkg/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
idmServicePassword, idpServicePassword, ocAdminServicePassword, revaServicePassword string
tokenManagerJwtSecret, collaborationWOPISecret, machineAuthAPIKey, systemUserAPIKey string
revaTransferSecret, thumbnailsTransferSecret, serviceAccountSecret, urlSigningSecret string
authGuestJWTSecret string
adminPasswdwordGenerated bool
)

Expand Down Expand Up @@ -103,6 +104,13 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
return fmt.Errorf("could not generate random secret for urlSigningSecret: %s", err)
}
}
authGuestJWTSecret = oldCfg.AuthGuest.TokenManager.JWTSecret
if authGuestJWTSecret == "" {
authGuestJWTSecret, err = generators.GenerateRandomPassword(passwordLength)
if err != nil {
return fmt.Errorf("could not generate random secret for authGuestJWTSecret: %s", err)
}
}
} else {
systemUserID = uuid.NewString()
adminUserID = uuid.NewString()
Expand Down Expand Up @@ -155,6 +163,10 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
if err != nil {
return fmt.Errorf("could not generate random secret for urlSigningSecret: %s", err)
}
authGuestJWTSecret, err = generators.GenerateRandomPassword(passwordLength)
if err != nil {
return fmt.Errorf("could not generate random secret for authGuestJWTSecret: %s", err)
}
thumbnailsTransferSecret, err = generators.GenerateRandomPassword(passwordLength)
if err != nil {
return fmt.Errorf("could not generate random password for thumbnailsTransferSecret: %s", err)
Expand Down Expand Up @@ -212,6 +224,10 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
},
},
},
AuthGuest: AuthGuest{
ServiceAccount: serviceAccount,
TokenManager: TokenManager{JWTSecret: authGuestJWTSecret},
},
Users: UsersAndGroupsService{
Drivers: LdapBasedService{
Ldap: LdapSettings{
Expand Down
7 changes: 7 additions & 0 deletions opencloud/pkg/init/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type OpenCloudConfig struct {
AuthBearer AuthbearerService `yaml:"auth_bearer"`
Users UsersAndGroupsService `yaml:"users"`
Groups UsersAndGroupsService `yaml:"groups"`
AuthGuest AuthGuest `yaml:"auth_guest"`
Ocm OcmService `yaml:"ocm"`
Thumbnails ThumbnailService `yaml:"thumbnails"`
Search Search `yaml:"search"`
Expand All @@ -53,6 +54,12 @@ type Activitylog struct {
ServiceAccount ServiceAccount `yaml:"service_account"`
}

// AuthGuest is the configuration for the auth-guest service
type AuthGuest struct {
ServiceAccount ServiceAccount `yaml:"service_account"`
TokenManager TokenManager `yaml:"token_manager"`
}

// App is the configuration for the collaboration service
type App struct {
Insecure bool `yaml:"insecure"`
Expand Down
8 changes: 8 additions & 0 deletions opencloud/pkg/runtime/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
audit "github.com/opencloud-eu/opencloud/services/audit/pkg/command"
authapp "github.com/opencloud-eu/opencloud/services/auth-app/pkg/command"
authbasic "github.com/opencloud-eu/opencloud/services/auth-basic/pkg/command"
authguest "github.com/opencloud-eu/opencloud/services/auth-guest/pkg/command"
authmachine "github.com/opencloud-eu/opencloud/services/auth-machine/pkg/command"
authservice "github.com/opencloud-eu/opencloud/services/auth-service/pkg/command"
clientlog "github.com/opencloud-eu/opencloud/services/clientlog/pkg/command"
Expand Down Expand Up @@ -198,6 +199,13 @@ func NewService(ctx context.Context, options ...Option) (*Service, error) {
cfg.Groups.Commons = cfg.Commons
return groups.Execute(cfg.Groups)
})
if opts.Config.Commons != nil && opts.Config.Commons.EnableGuestLinks {

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.

This doesn't currently seem to work. The opts.Config.Commons struct is still nil here (unless the yaml config file has a section named shared).

AFAICS this is a bug in the top-level config parser ("pkg/config/parser/parse.go") which does not initialize cfg.Commons in EnsureDefaults().

reg(3, opts.Config.AuthGuest.Service.Name, func(ctx context.Context, cfg *occfg.Config) error {
cfg.AuthGuest.Context = ctx
cfg.AuthGuest.Commons = cfg.Commons
return authguest.Execute(cfg.AuthGuest)
})
}
reg(3, opts.Config.IDM.Service.Name, func(ctx context.Context, cfg *occfg.Config) error {
cfg.IDM.Context = ctx
cfg.IDM.Commons = cfg.Commons
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
authapp "github.com/opencloud-eu/opencloud/services/auth-app/pkg/config"
authbasic "github.com/opencloud-eu/opencloud/services/auth-basic/pkg/config"
authbearer "github.com/opencloud-eu/opencloud/services/auth-bearer/pkg/config"
authguest "github.com/opencloud-eu/opencloud/services/auth-guest/pkg/config"
authmachine "github.com/opencloud-eu/opencloud/services/auth-machine/pkg/config"
authservice "github.com/opencloud-eu/opencloud/services/auth-service/pkg/config"
clientlog "github.com/opencloud-eu/opencloud/services/clientlog/pkg/config"
Expand Down Expand Up @@ -99,6 +100,7 @@ type Config struct {
Gateway *gateway.Config `yaml:"gateway"`
Graph *graph.Config `yaml:"graph"`
Groups *groups.Config `yaml:"groups"`
AuthGuest *authguest.Config `yaml:"auth_guest"`
IDM *idm.Config `yaml:"idm"`
IDP *idp.Config `yaml:"idp"`
Invitations *invitations.Config `yaml:"invitations"`
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/defaultconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
authapp "github.com/opencloud-eu/opencloud/services/auth-app/pkg/config/defaults"
authbasic "github.com/opencloud-eu/opencloud/services/auth-basic/pkg/config/defaults"
authbearer "github.com/opencloud-eu/opencloud/services/auth-bearer/pkg/config/defaults"
authguest "github.com/opencloud-eu/opencloud/services/auth-guest/pkg/config/defaults"
authmachine "github.com/opencloud-eu/opencloud/services/auth-machine/pkg/config/defaults"
authservice "github.com/opencloud-eu/opencloud/services/auth-service/pkg/config/defaults"
clientlog "github.com/opencloud-eu/opencloud/services/clientlog/pkg/config/defaults"
Expand Down Expand Up @@ -74,6 +75,7 @@ func DefaultConfig() *Config {
Gateway: gateway.DefaultConfig(),
Graph: graph.DefaultConfig(),
Groups: groups.DefaultConfig(),
AuthGuest: authguest.DefaultConfig(),
IDM: idm.DefaultConfig(),
IDP: idp.DefaultConfig(),
Invitations: invitations.DefaultConfig(),
Expand Down
6 changes: 3 additions & 3 deletions pkg/config/parser/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ func EnsureDefaults(cfg *config.Config) {
if cfg.Reva == nil {
cfg.Reva = &shared.Reva{}
}
}

// EnsureCommons copies applicable parts of the OpenCloud config into the commons part
func EnsureCommons(cfg *config.Config) {
// ensure the commons part is initialized
if cfg.Commons == nil {
cfg.Commons = &shared.Commons{}
}
}

// EnsureCommons copies applicable parts of the OpenCloud config into the commons part
func EnsureCommons(cfg *config.Config) {
cfg.Commons.Log = structs.CopyOrZeroValue(cfg.Log)
cfg.Commons.Cache = structs.CopyOrZeroValue(cfg.Cache)

Expand Down
17 changes: 17 additions & 0 deletions pkg/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
)

Expand All @@ -20,3 +21,19 @@ func (ResourceMention) Unmarshal(v []byte) (interface{}, error) {
err := json.Unmarshal(v, &e)
return e, err
}

type GuestTokenCreated struct {
ShareID *collaboration.ShareId
Sharer *user.UserId
GranteeEmail string
ItemID *provider.ResourceId
ResourceName string
Token string

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.

This event should include the grantee's email adress so the notification service doesn't have to look up the share anymore.

Timestamp time.Time
}

func (GuestTokenCreated) Unmarshal(v []byte) (interface{}, error) {
e := GuestTokenCreated{}
err := json.Unmarshal(v, &e)
return e, err
}
1 change: 1 addition & 0 deletions pkg/shared/shared_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type Commons struct {
SystemUserAPIKey string `mask:"password" yaml:"system_user_api_key" env:"SYSTEM_USER_API_KEY" desc:"API key for all system users." introductionVersion:"1.0.0"`
AdminUserID string `yaml:"admin_user_id" env:"OC_ADMIN_USER_ID" desc:"ID of a user, that should receive admin privileges. Consider that the UUID can be encoded in some LDAP deployment configurations like in .ldif files. These need to be decoded beforehand." introductionVersion:"1.0.0"`
MultiTenantEnabled bool `yaml:"multi_tenant_enabled" env:"OC_MULTI_TENANT_ENABLED" desc:"Set this to true to enable multi-tenant support." introductionVersion:"4.0.0"`
EnableGuestLinks bool `yaml:"enable_guest_links" env:"OC_ENABLE_GUEST_LINKS" desc:"Enables the guest links feature: creating shares to mail addresses and redeeming guest links. Disabled by default." introductionVersion:"%%NEXT%%"`

// NOTE: you will not fing GRPCMaxReceivedMessageSize size being used in the code. The envvar is actually extracted in revas `pool` package: https://github.com/cs3org/reva/blob/edge/pkg/rgrpc/todo/pool/connection.go
// It is mentioned here again so it is documented
Expand Down
14 changes: 14 additions & 0 deletions services/auth-guest/.mockery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# maintain v2 separate mocks dir
dir: "{{.InterfaceDir}}/mocks"
structname: "{{.InterfaceName}}"
filename: "{{.InterfaceName | snakecase }}.go"
pkgname: mocks

template: testify
packages:
github.com/opencloud-eu/opencloud/services/auth-guest/pkg/service/authguest:
interfaces:
AuthGuest: {}
github.com/opencloud-eu/opencloud/services/auth-guest/pkg/service/storage:
interfaces:
Manager: {}
15 changes: 15 additions & 0 deletions services/auth-guest/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
SHELL := bash
NAME := auth-guest

ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI
include ../../.bingo/Variables.mk
endif

include ../../.make/default.mk
include ../../.make/go.mk
include ../../.make/release.mk
include ../../.make/docs.mk

.PHONY: go-generate
go-generate: $(MOCKERY)
$(MOCKERY)
101 changes: 101 additions & 0 deletions services/auth-guest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# auth-guest

The `auth-guest` service gives guest users access to a share without a full
OpenCloud account. When a share is created for a user of type
`USER_TYPE_GUEST`, the service issues a one-time guest link token; redeeming
that token exchanges it for a signed session cookie that authenticates the
guest.

It is disabled by default. Set `OC_ENABLE_GUEST_LINKS=true` to enable the guest
links feature and start the service.

## Overview

- **Consumes** the share lifecycle events `ShareCreated`, `ShareRemoved` and
`ShareExpired`.
- **Publishes** the `GuestTokenCreated` event carrying the guest link token,
so the link can be delivered to the guest.
- Exposes an unauthenticated endpoint that redeems the token and sets a
session cookie.
- Stores only hashes of the token and deletes the stored record when the share
is removed or expires.

## Guest links flow

The following sequence diagram describes the guest links flow:

```mermaid
sequenceDiagram
autonumber
actor User as Guest user
participant Web as Web client
participant Redeem as Redeem endpoint
participant Proxy as OpenCloud proxy
participant Graph as Graph / sharedWithMe
participant DAV as WebDAV
participant Reva as Reva

User->>Web: Open guest link with valid token
Web->>+Redeem: Redeem Token
Note right of Redeem: Validate Token
Redeem->>+Reva: Get Share
Reva->>-Redeem: Share
Note right of Redeem: Validate Share, Mark Token used
Redeem->>-Web: Set Cookie, return shareid
Note right of Web: HTTP only cookie with signed JWT (JWT lifetime 24h)
Web->>+Proxy: "/graph/me/drives/sharedWithMe"
Proxy->>+Reva: validate token extracted from JWT
Note right of Reva: Sign Reva Token for Guest User
Reva->>-Proxy: Authenticated
Proxy->>+Graph: "/graph/me/drives/sharedWithMe"
Note right of Proxy: Using Reva Token
Graph->>+Reva: Requests to ShareProvider
Reva->>-Graph: Shares
Graph->>-Proxy: driveItems (all shares for the Guest User)
Proxy->>-Web: driveItems
Note right of Web: Extracts driveItem for the specific share
Web->>+Proxy: PROPFIND (resource id extracted from driveItem)
Note right of Web: Using Cookie
Proxy->>+Reva: validate token extracted from JWT
Note right of Reva: Sign Reva Token for Guest User
Reva->>-Proxy: Authenticated
Proxy->>+DAV: PROPFIND
Note right of Proxy: Using Reva Token
DAV->>+Reva: Requests to StorageProvider
Reva->>-DAV: StorageProvider Responses
DAV->>-Proxy: PROPFIND Response
Proxy->>-Web: PROPFIND Response
```

## Token lifecycle

1. **Issue** — on the consumed `ShareCreated` event, where the grantee is a
guest, the service generates a random secret and stores a record keyed by
the hash of the share id. It then publishes the `GuestTokenCreated` event
with the token.
2. **Redeem** — the guest posts the token to
`POST /graph/v1beta1/extensions/org.libregraph/guestLinks/redeem`.
The service validates the token and the share, marks the token as used
and returns a signed JWT session token in a cookie plus the share's
`permissionId` in the response body. Tokens are single-use.
3. **Cleanup** — on the consumed `ShareRemoved` or `ShareExpired` event, the
stored record is deleted.

## Configuration

The service is configured via `AUTH_GUEST_*` environment variables or a
`auth-guest.yaml` file.

To run only the HTTP part, set `AUTH_GUEST_EVENTS_DISABLED=true`. To run only
the event consumer, set `AUTH_GUEST_HTTP_DISABLED=true`.

Relevant options:

- `AUTH_GUEST_JWT_SECRET` — secret used to sign session tokens.
- `AUTH_GUEST_JWT_COOKIE_NAME`, `AUTH_GUEST_JWT_TTL` — session cookie name and
lifetime.
- `AUTH_GUEST_TOKENS_STORAGE_ROOT` — where guest link token records are stored.
- `AUTH_GUEST_SERVICE_ACCOUNT_ID`, `AUTH_GUEST_SERVICE_ACCOUNT_SECRET` — service
account used to query the gateway for share metadata.
- `AUTH_GUEST_NUM_CONSUMERS` — number of concurrent event consumers.
- `OC_REVA_GATEWAY` — CS3 gateway used to look up shares.
21 changes: 21 additions & 0 deletions services/auth-guest/pkg/command/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2026 OpenCloud GmbH <mail@opencloud.eu>
// SPDX-License-Identifier: Apache-2.0

package command

import (
"github.com/opencloud-eu/opencloud/services/auth-guest/pkg/config"
"github.com/spf13/cobra"
)

// Health is the entrypoint for the health command.
func Health(cfg *config.Config) *cobra.Command {
return &cobra.Command{
Use: "health",
Short: "Check health status",
RunE: func(cmd *cobra.Command, args []string) error {
// not implemented
return nil
},
}
}
Loading