diff options
Diffstat
| -rw-r--r-- | .gitignore | 9 | +5 −4 |
| -rw-r--r-- | README.md | 12 | +6 −6 |
| -rw-r--r-- | REUSE.toml | 4 | +2 −2 |
| -rw-r--r-- | Taskfile.yml | 70 | +37 −33 |
| -rw-r--r-- | buf.lock (renamed from proto/buf.lock) | 0 | +0 −0 |
| -rw-r--r-- | buf.yaml (renamed from proto/buf.yaml) | 3 | +3 −0 |
| -rw-r--r-- | cmd/mirum-server/actor.go (renamed from cmd/mirumd/actor.go) | 52 | +26 −26 |
| -rw-r--r-- | cmd/mirum-server/api_cli.go (renamed from cmd/mirumd/api_cli.go) | 38 | +19 −19 |
| -rw-r--r-- | cmd/mirum-server/cert.go (renamed from cmd/mirumd/cert.go) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/cert_test.go (renamed from cmd/mirumd/cert_test.go) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/config.go (renamed from cmd/mirumd/config.go) | 2 | +1 −1 |
| -rw-r--r-- | cmd/mirum-server/database.go (renamed from cmd/mirumd/database.go) | 32 | +16 −16 |
| -rw-r--r-- | cmd/mirum-server/id.go (renamed from cmd/mirumd/id.go) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/id_test.go (renamed from cmd/mirumd/id_test.go) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/main.go (renamed from cmd/mirumd/main.go) | 26 | +13 −13 |
| -rw-r--r-- | cmd/mirum-server/proto/api.proto (renamed from proto/admin.proto) | 6 | +3 −3 |
| -rw-r--r-- | cmd/mirum-server/proto/buf.gen.yaml (renamed from proto/buf.gen.yaml) | 6 | +3 −3 |
| -rw-r--r-- | cmd/mirum-server/server.go (renamed from cmd/mirumd/server.go) | 6 | +3 −3 |
| -rw-r--r-- | cmd/mirum-server/server_admin.go (renamed from cmd/mirumd/server_admin.go) | 192 | +96 −96 |
| -rw-r--r-- | cmd/mirum-server/server_grpc.go (renamed from cmd/mirumd/server_grpc.go) | 14 | +7 −7 |
| -rw-r--r-- | cmd/mirum-server/server_web.go (renamed from cmd/mirumd/server_web.go) | 18 | +9 −9 |
| -rw-r--r-- | cmd/mirum-server/static.go (renamed from cmd/mirumd/static.go) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/static_dev.go (renamed from cmd/mirumd/static_dev.go) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/static_prod.go (renamed from cmd/mirumd/static_prod.go) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/api/client.ts (renamed from cmd/mirumd/web/api/client.ts) | 6 | +3 −3 |
| -rw-r--r-- | cmd/mirum-server/web/components.json (renamed from cmd/mirumd/web/components.json) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/components/pages/dashboard.tsx (renamed from cmd/mirumd/web/components/pages/dashboard.tsx) | 6 | +3 −3 |
| -rw-r--r-- | cmd/mirum-server/web/components/pages/error.tsx (renamed from cmd/mirumd/web/components/pages/error.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/components/pages/login.tsx (renamed from cmd/mirumd/web/components/pages/login.tsx) | 2 | +1 −1 |
| -rw-r--r-- | cmd/mirum-server/web/components/ui/alert.tsx (renamed from cmd/mirumd/web/components/ui/alert.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/components/ui/button.tsx (renamed from cmd/mirumd/web/components/ui/button.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/components/ui/card.tsx (renamed from cmd/mirumd/web/components/ui/card.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/components/ui/field.tsx (renamed from cmd/mirumd/web/components/ui/field.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/components/ui/input.tsx (renamed from cmd/mirumd/web/components/ui/input.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/components/ui/label.tsx (renamed from cmd/mirumd/web/components/ui/label.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/components/ui/separator.tsx (renamed from cmd/mirumd/web/components/ui/separator.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/entries/dashboard.tsx (renamed from cmd/mirumd/web/entries/dashboard.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/entries/error.tsx (renamed from cmd/mirumd/web/entries/error.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/entries/login.tsx (renamed from cmd/mirumd/web/entries/login.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/index.css (renamed from cmd/mirumd/web/index.css) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/lib/errors.ts (renamed from cmd/mirumd/web/lib/errors.ts) | 2 | +1 −1 |
| -rw-r--r-- | cmd/mirum-server/web/lib/mount.tsx (renamed from cmd/mirumd/web/lib/mount.tsx) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/lib/utils.ts (renamed from cmd/mirumd/web/lib/utils.ts) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/package-lock.json (renamed from cmd/mirumd/web/package-lock.json) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/package.json (renamed from cmd/mirumd/web/package.json) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/shell.html (renamed from cmd/mirumd/web/shell.html) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/tsconfig.json (renamed from cmd/mirumd/web/tsconfig.json) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-server/web/vite.config.ts (renamed from cmd/mirumd/web/vite.config.ts) | 0 | +0 −0 |
| -rw-r--r-- | cmd/mirum-worker/client.go (renamed from cmd/mirumw/client.go) | 12 | +6 −6 |
| -rw-r--r-- | cmd/mirum-worker/config.go (renamed from cmd/mirumw/config.go) | 2 | +1 −1 |
| -rw-r--r-- | cmd/mirum-worker/main.go (renamed from cmd/mirumw/main.go) | 0 | +0 −0 |
| -rw-r--r-- | internal/config/config.go | 2 | +1 −1 |
| -rw-r--r-- | internal/protocol/platform.go | 76 | +38 −38 |
| -rw-r--r-- | internal/protocol/proto/buf.gen.yaml | 11 | +11 −0 |
| -rw-r--r-- | internal/protocol/proto/wire.proto (renamed from proto/mirum.proto) | 6 | +3 −3 |
| -rw-r--r-- | nfpm.yaml | 40 | +20 −20 |
| -rw-r--r-- | pkg/mirum-server.service (renamed from pkg/mirumd.service) | 10 | +5 −5 |
| -rw-r--r-- | pkg/mirum-worker@.service (renamed from pkg/mirumw@.service) | 8 | +4 −4 |
| -rw-r--r-- | pkg/scripts/postinstall.sh | 6 | +3 −3 |
| -rw-r--r-- | pkg/scripts/preinstall.sh | 2 | +1 −1 |
| -rw-r--r-- | pkg/scripts/preremove.sh | 8 | +4 −4 |
| -rw-r--r-- | pkg/server/config.yaml (renamed from pkg/mirumd.yaml) | 4 | +2 −2 |
| -rw-r--r-- | pkg/worker/default.yaml (renamed from pkg/mirumw-default.yaml) | 10 | +5 −5 |
63 files changed, 361 insertions, 342 deletions
diff --git a/.gitignore b/.gitignore index cec9f17..e9e681e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,10 @@ /dev /build /.task -cmd/mirumd/static -cmd/mirumd/web/node_modules +cmd/mirum-server/static +cmd/mirum-server/web/node_modules # grpc generated code -/internal/protocol/pb -/cmd/mirumd/web/gen +/internal/protocol/wirepb +/cmd/mirum-server/apipb +/cmd/mirum-server/web/gen diff --git a/README.md b/README.md index 4e30f73..8292f0d 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ echo "deb [signed-by=/usr/share/keyrings/mirum.gpg] https://dl.mirum.dev/apt/ ni sudo apt update && sudo apt install mirum # Start the server -sudo systemctl enable --now mirumd +sudo systemctl enable --now mirum-server # Start a worker (optional, can run on a different host) -sudo systemctl enable --now mirumw@default +sudo systemctl enable --now mirum-worker@default ``` **Fedora/RHEL:** @@ -34,10 +34,10 @@ sudo curl -o /etc/yum.repos.d/mirum-nightly.repo https://dl.mirum.dev/rpm/nightl sudo dnf install mirum # Start the server -sudo systemctl enable --now mirumd +sudo systemctl enable --now mirum-server # Start a worker (optional, can run on a different host) -sudo systemctl enable --now mirumw@default +sudo systemctl enable --now mirum-worker@default ``` **openSUSE:** @@ -49,10 +49,10 @@ sudo zypper refresh sudo zypper install mirum # Start the server -sudo systemctl enable --now mirumd +sudo systemctl enable --now mirum-server # Start a worker (optional, can run on a different host) -sudo systemctl enable --now mirumw@default +sudo systemctl enable --now mirum-worker@default ``` ## Contributing diff --git a/REUSE.toml b/REUSE.toml index 455fc7e..2be7092 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -10,12 +10,12 @@ path = [ "CONCEPT.md", "README.md", "VERSION", - "cmd/mirumd/web/*.json", + "cmd/mirum-server/web/*.json", "go.mod", "go.sum", "pkg/dl/*", "pkg/logo.svg", - "proto/buf.*", + "buf.*", ] SPDX-FileCopyrightText = "2026 Nikolay Govorov <me@govorov.online>" SPDX-License-Identifier = "AGPL-3.0-or-later" diff --git a/Taskfile.yml b/Taskfile.yml index b3090e6..8178989 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -70,22 +70,26 @@ tasks: proto: desc: Generate ConnectRPC code from proto files sources: - - proto/*.proto - - proto/buf.gen.yaml - - proto/buf.yaml + - internal/protocol/proto/*.proto + - internal/protocol/proto/buf.gen.yaml + - cmd/mirum-server/proto/*.proto + - cmd/mirum-server/proto/buf.gen.yaml + - buf.yaml generates: - - internal/protocol/pb/**/*.go - - cmd/mirumd/web/gen/**/*.ts + - internal/protocol/wirepb/**/*.go + - cmd/mirum-server/apipb/**/*.go + - cmd/mirum-server/web/gen/**/*.ts cmds: - - rm -rf internal/protocol/pb cmd/mirumd/web/gen - - cd proto && buf generate - # admin_pb.ts imports file_buf_validate_validate from buf/validate/validate_pb.js. + - rm -rf internal/protocol/wirepb cmd/mirum-server/apipb cmd/mirum-server/web/gen + - cd internal/protocol/proto && buf generate + - cd cmd/mirum-server/proto && buf generate + # api_pb.ts imports file_buf_validate_validate from buf/validate/validate_pb.js. # buf.validate carries only server-side field annotations that the browser # never decodes, so replace the 200+ KB generated schema with an empty # descriptor stub that satisfies the GenFile type. - - mkdir -p cmd/mirumd/web/gen/buf/validate + - mkdir -p cmd/mirum-server/web/gen/buf/validate - | - cat > cmd/mirumd/web/gen/buf/validate/validate_pb.ts <<'EOF' + cat > cmd/mirum-server/web/gen/buf/validate/validate_pb.ts <<'EOF' // Stub: see Taskfile.yml proto task. buf.validate is server-only. import { fileDesc, type GenFile } from "@bufbuild/protobuf/codegenv2" export const file_buf_validate_validate: GenFile = /*@__PURE__*/ fileDesc("") @@ -93,7 +97,7 @@ tasks: web:install: desc: Install frontend dependencies - dir: cmd/mirumd/web + dir: cmd/mirum-server/web sources: - package.json - package-lock.json @@ -103,8 +107,8 @@ tasks: - npm ci web:build: - desc: Build the frontend into cmd/mirumd/static - dir: cmd/mirumd/web + desc: Build the frontend into cmd/mirum-server/static + dir: cmd/mirum-server/web deps: [web:install, proto] sources: - api/**/* @@ -123,7 +127,7 @@ tasks: web:size: desc: Check frontend bundle size budgets - dir: cmd/mirumd/web + dir: cmd/mirum-server/web deps: [web:build] sources: - ../static/assets/* @@ -132,8 +136,8 @@ tasks: - npm run size web:dev: - desc: Run Vite dev server (use with `go run -tags dev ./cmd/mirumd`) - dir: cmd/mirumd/web + desc: Run Vite dev server (use with `go run -tags dev ./cmd/mirum-server`) + dir: cmd/mirum-server/web deps: [web:install, proto] prefix: vite env: @@ -154,8 +158,8 @@ tasks: test -f dev/server.crt || openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout dev/server.key -out dev/server.crt - -days 365 -nodes -subj "/CN=mirumd" - -addext "subjectAltName=DNS:mirumd,DNS:localhost" + -days 365 -nodes -subj "/CN=mirum-server" + -addext "subjectAltName=DNS:mirum-server,DNS:localhost" - >- test -f dev/worker.key || openssl genpkey -algorithm ed25519 -out dev/worker.key @@ -164,13 +168,13 @@ tasks: openssl pkey -in dev/worker.key -pubout -out dev/worker.pub devenv:config: - desc: Write dev/mirumd.yaml if missing + desc: Write dev/mirum-server.yaml if missing status: - - test -f dev/mirumd.yaml + - test -f dev/mirum-server.yaml cmds: - mkdir -p dev - | - cat > dev/mirumd.yaml <<EOF + cat > dev/mirum-server.yaml <<EOF web_addr: ":3000" grpc_addr: ":2026" admin_socket: "/tmp/mirum-admin.sock" @@ -199,18 +203,18 @@ tasks: deps: [devenv:keys, devenv:config, devenv:db] dev:server: - desc: Build mirumd with -tags dev and run it against the dev environment + desc: Build mirum-server with -tags dev and run it against the dev environment deps: [proto, devenv] - prefix: mirumd + prefix: mirum-server env: CGO_ENABLED: "0" cmds: - mkdir -p {{.BUILD_DIR}} - - go build -tags dev -o {{.BUILD_DIR}}/mirumd ./cmd/mirumd - - "{{.BUILD_DIR}}/mirumd daemon --config dev/mirumd.yaml" + - go build -tags dev -o {{.BUILD_DIR}}/mirum-server ./cmd/mirum-server + - "{{.BUILD_DIR}}/mirum-server daemon --config dev/mirum-server.yaml" dev: - desc: Run Vite dev server and mirumd together (HMR-enabled) + desc: Run Vite dev server and mirum-server together (HMR-enabled) deps: [web:dev, dev:server] build: @@ -225,16 +229,16 @@ tasks: GOARCH: "{{.GOARCH}}" cmds: - mkdir -p {{.BUILD_DIR}} - - go build -o {{.BUILD_DIR}}/mirumd-{{.GOOS}}-{{.GOARCH}} ./cmd/mirumd - - go build -o {{.BUILD_DIR}}/mirumw-{{.GOOS}}-{{.GOARCH}} ./cmd/mirumw - - cp {{.BUILD_DIR}}/mirumd-{{.GOOS}}-{{.GOARCH}} {{.BUILD_DIR}}/mirumd - - cp {{.BUILD_DIR}}/mirumw-{{.GOOS}}-{{.GOARCH}} {{.BUILD_DIR}}/mirumw + - go build -o {{.BUILD_DIR}}/mirum-server-{{.GOOS}}-{{.GOARCH}} ./cmd/mirum-server + - go build -o {{.BUILD_DIR}}/mirum-worker-{{.GOOS}}-{{.GOARCH}} ./cmd/mirum-worker + - cp {{.BUILD_DIR}}/mirum-server-{{.GOOS}}-{{.GOARCH}} {{.BUILD_DIR}}/mirum-server + - cp {{.BUILD_DIR}}/mirum-worker-{{.GOOS}}-{{.GOARCH}} {{.BUILD_DIR}}/mirum-worker run: - desc: Build and run mirumd against the dev environment + desc: Build and run mirum-server against the dev environment deps: [build, devenv] cmds: - - "{{.BUILD_DIR}}/mirumd daemon --config dev/mirumd.yaml" + - "{{.BUILD_DIR}}/mirum-server daemon --config dev/mirum-server.yaml" test: desc: Run all tests @@ -282,7 +286,7 @@ tasks: task build GOOS=linux GOARCH=$arch ARCH=$arch nfpm package --packager deb --target {{.DIST_DIR}}/ ARCH=$arch nfpm package --packager rpm --target {{.DIST_DIR}}/ - rm {{.BUILD_DIR}}/mirumd {{.BUILD_DIR}}/mirumw + rm {{.BUILD_DIR}}/mirum-server {{.BUILD_DIR}}/mirum-worker done printf 'VERSION={{.VERSION}}\nCHANNEL={{.CHANNEL}}\n' > {{.DIST_DIR}}/build.env diff --git a/proto/buf.lock b/buf.lock index d15a117..d15a117 100644 --- a/proto/buf.lock +++ b/buf.lock diff --git a/proto/buf.yaml b/buf.yaml index 8d594f7..ddd7a3c 100644 --- a/proto/buf.yaml +++ b/buf.yaml @@ -2,5 +2,8 @@ # SPDX-License-Identifier: AGPL-3.0-or-later version: v2 +modules: + - path: internal/protocol/proto + - path: cmd/mirum-server/proto deps: - buf.build/bufbuild/protovalidate diff --git a/cmd/mirumd/actor.go b/cmd/mirum-server/actor.go index bdd49c2..03d30ba 100644 --- a/cmd/mirumd/actor.go +++ b/cmd/mirum-server/actor.go @@ -8,7 +8,7 @@ import ( "errors" "slices" - "dimidiumlabs/mirum/internal/protocol/pb" + "dimidiumlabs/mirum/cmd/mirum-server/apipb" "github.com/google/uuid" "github.com/jackc/pgx/v5" @@ -19,40 +19,40 @@ var ( ErrUnauthenticated = errors.New("database: authentication required") ) -var anonPermissions = []pb.Perm{ - pb.Perm_PERM_ORG_READ, +var anonPermissions = []apipb.Perm{ + apipb.Perm_PERM_ORG_READ, } -var userGlobalPermissions = []pb.Perm{ - pb.Perm_PERM_ORG_READ, - pb.Perm_PERM_ORG_WRITE, - pb.Perm_PERM_USER_READ, +var userGlobalPermissions = []apipb.Perm{ + apipb.Perm_PERM_ORG_READ, + apipb.Perm_PERM_ORG_WRITE, + apipb.Perm_PERM_USER_READ, } // rolePermissions is the single source of truth for role → perm bundles. // RLS checks only tenancy (membership); action authz lives here. -var rolePermissions = map[string][]pb.Perm{ +var rolePermissions = map[string][]apipb.Perm{ "owner": { - pb.Perm_PERM_ORG_READ, - pb.Perm_PERM_ORG_WRITE, - pb.Perm_PERM_ORG_DELETE, - pb.Perm_PERM_ORG_MEMBER_READ, - pb.Perm_PERM_ORG_MEMBER_WRITE, - pb.Perm_PERM_WORKER_READ, - pb.Perm_PERM_WORKER_WRITE, + apipb.Perm_PERM_ORG_READ, + apipb.Perm_PERM_ORG_WRITE, + apipb.Perm_PERM_ORG_DELETE, + apipb.Perm_PERM_ORG_MEMBER_READ, + apipb.Perm_PERM_ORG_MEMBER_WRITE, + apipb.Perm_PERM_WORKER_READ, + apipb.Perm_PERM_WORKER_WRITE, }, "admin": { - pb.Perm_PERM_ORG_READ, - pb.Perm_PERM_ORG_WRITE, - pb.Perm_PERM_ORG_MEMBER_READ, - pb.Perm_PERM_ORG_MEMBER_WRITE, - pb.Perm_PERM_WORKER_READ, - pb.Perm_PERM_WORKER_WRITE, + apipb.Perm_PERM_ORG_READ, + apipb.Perm_PERM_ORG_WRITE, + apipb.Perm_PERM_ORG_MEMBER_READ, + apipb.Perm_PERM_ORG_MEMBER_WRITE, + apipb.Perm_PERM_WORKER_READ, + apipb.Perm_PERM_WORKER_WRITE, }, "member": { - pb.Perm_PERM_ORG_READ, - pb.Perm_PERM_ORG_MEMBER_READ, - pb.Perm_PERM_WORKER_READ, + apipb.Perm_PERM_ORG_READ, + apipb.Perm_PERM_ORG_MEMBER_READ, + apipb.Perm_PERM_WORKER_READ, }, } @@ -176,7 +176,7 @@ func (a Actor) kindString() string { } // checkGlobal checks a global-scope perm (no specific org). Pure, no DB. -func checkGlobal(actor Actor, perm pb.Perm) error { +func checkGlobal(actor Actor, perm apipb.Perm) error { switch actor.kind { case actorOperator, actorSystem: return nil @@ -199,7 +199,7 @@ func checkGlobal(actor Actor, perm pb.Perm) error { } // checkPerm checks an org-scoped perm within an existing transaction. -func checkPerm(ctx context.Context, tx pgx.Tx, actor Actor, orgID OrgID, perm pb.Perm) error { +func checkPerm(ctx context.Context, tx pgx.Tx, actor Actor, orgID OrgID, perm apipb.Perm) error { switch actor.kind { case actorOperator, actorSystem: return nil diff --git a/cmd/mirumd/api_cli.go b/cmd/mirum-server/api_cli.go index 6d53511..8220c65 100644 --- a/cmd/mirumd/api_cli.go +++ b/cmd/mirum-server/api_cli.go @@ -3,10 +3,10 @@ package main -// Admin CLI is generated from admin.proto at startup via protoreflect. +// Console CLI is generated from api.proto at startup via protoreflect. // RPC name is camelCase-split into a cobra path: UserCreate -> "user create", // OrgMemberAdd -> "org member add". Flags come from request fields, dispatch -// goes through reflect on pbconnect.AdminClient. +// goes through reflect on apipbconnect.ConsoleClient. import ( "context" @@ -26,14 +26,14 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/types/known/timestamppb" - "dimidiumlabs/mirum/internal/protocol/pb" - "dimidiumlabs/mirum/internal/protocol/pb/pbconnect" + "dimidiumlabs/mirum/cmd/mirum-server/apipb" + "dimidiumlabs/mirum/cmd/mirum-server/apipb/apipbconnect" ) // mkClient is called per-invocation so persistent flags (e.g. --socket) are // already parsed by the time it runs. -func buildAdminCLI(root *cobra.Command, mkClient func() pbconnect.AdminClient) { - methods := pb.File_admin_proto.Services().ByName("Admin").Methods() +func buildConsoleCLI(root *cobra.Command, mkClient func() apipbconnect.ConsoleClient) { + methods := apipb.File_api_proto.Services().ByName("Console").Methods() for i := 0; i < methods.Len(); i++ { md := methods.Get(i) path := splitCamel(string(md.Name())) @@ -77,7 +77,7 @@ func ensureGroups(root *cobra.Command, path []string) *cobra.Command { // fieldSetter writes one flag value into the request message. type fieldSetter func(*pflag.FlagSet, protoreflect.Message) error -func buildMethodCmd(md protoreflect.MethodDescriptor, leaf string, mkClient func() pbconnect.AdminClient) *cobra.Command { +func buildMethodCmd(md protoreflect.MethodDescriptor, leaf string, mkClient func() apipbconnect.ConsoleClient) *cobra.Command { reqDesc := md.Input() rpcName := string(md.Name()) cmd := &cobra.Command{ @@ -91,7 +91,7 @@ func buildMethodCmd(md protoreflect.MethodDescriptor, leaf string, mkClient func fmt.Fprintln(os.Stderr, err) os.Exit(1) } - resp, err := dispatchAdmin(mkClient(), rpcName, req) + resp, err := dispatchConsole(mkClient(), rpcName, req) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) @@ -187,7 +187,7 @@ func registerField(cmd *cobra.Command, fd protoreflect.FieldDescriptor) fieldSet } if required { - panic(fmt.Sprintf("admincli: unhandled required field %s (kind=%s)", fd.FullName(), fd.Kind())) + panic(fmt.Sprintf("consolecli: unhandled required field %s (kind=%s)", fd.FullName(), fd.Kind())) } return nil } @@ -204,7 +204,7 @@ func registerMessageField(cmd *cobra.Command, fd protoreflect.FieldDescriptor, f } switch fd.Message().FullName() { - case "mirum.UserRef": + case "mirum.api.UserRef": flags.String(flagName, "", "user email") markRequired() return func(fs *pflag.FlagSet, m protoreflect.Message) error { @@ -212,12 +212,12 @@ func registerMessageField(cmd *cobra.Command, fd protoreflect.FieldDescriptor, f if v == "" { return nil } - ref := &pb.UserRef{Ref: &pb.UserRef_Email{Email: v}} + ref := &apipb.UserRef{Ref: &apipb.UserRef_Email{Email: v}} m.Set(fd, protoreflect.ValueOfMessage(ref.ProtoReflect())) return nil } - case "mirum.OrgRef": + case "mirum.api.OrgRef": flags.String(flagName, "", "org slug") markRequired() return func(fs *pflag.FlagSet, m protoreflect.Message) error { @@ -225,22 +225,22 @@ func registerMessageField(cmd *cobra.Command, fd protoreflect.FieldDescriptor, f if v == "" { return nil } - ref := &pb.OrgRef{Ref: &pb.OrgRef_Slug{Slug: v}} + ref := &apipb.OrgRef{Ref: &apipb.OrgRef_Slug{Slug: v}} m.Set(fd, protoreflect.ValueOfMessage(ref.ProtoReflect())) return nil } - case "mirum.PageRequest": + case "mirum.api.PageRequest": return nil } if required { - panic(fmt.Sprintf("admincli: unhandled required message field %s (type=%s)", fd.FullName(), fd.Message().FullName())) + panic(fmt.Sprintf("consolecli: unhandled required message field %s (type=%s)", fd.FullName(), fd.Message().FullName())) } return nil } -// Admin schema uses bytes only for UUIDs (id / *_id) and ed25519 PKIX keys. +// Console schema uses bytes only for UUIDs (id / *_id) and ed25519 PKIX keys. func parseBytesFlag(fieldName, v string) ([]byte, error) { switch { case fieldName == "id" || strings.HasSuffix(fieldName, "_id"): @@ -297,11 +297,11 @@ func buildRequest(desc protoreflect.MessageDescriptor, flags *pflag.FlagSet, set // reflect.New on *connect.Request[T] is equivalent to connect.NewRequest(req): // Msg is the only public field, the rest are initialised lazily at send time. -func dispatchAdmin(client pbconnect.AdminClient, name string, req proto.Message) (proto.Message, error) { +func dispatchConsole(client apipbconnect.ConsoleClient, name string, req proto.Message) (proto.Message, error) { cv := reflect.ValueOf(client) method := cv.MethodByName(name) if !method.IsValid() { - return nil, fmt.Errorf("unknown admin method %q", name) + return nil, fmt.Errorf("unknown console method %q", name) } // method signature: // func(context.Context, *connect.Request[T]) (*connect.Response[U], error) @@ -329,7 +329,7 @@ func printResponse(resp proto.Message) { var meaningful []protoreflect.FieldDescriptor for i := 0; i < fields.Len(); i++ { f := fields.Get(i) - if f.Kind() == protoreflect.MessageKind && f.Message().FullName() == "mirum.PageResponse" { + if f.Kind() == protoreflect.MessageKind && f.Message().FullName() == "mirum.api.PageResponse" { continue } meaningful = append(meaningful, f) diff --git a/cmd/mirumd/cert.go b/cmd/mirum-server/cert.go index 67128d5..67128d5 100644 --- a/cmd/mirumd/cert.go +++ b/cmd/mirum-server/cert.go diff --git a/cmd/mirumd/cert_test.go b/cmd/mirum-server/cert_test.go index 794b2f6..794b2f6 100644 --- a/cmd/mirumd/cert_test.go +++ b/cmd/mirum-server/cert_test.go diff --git a/cmd/mirumd/config.go b/cmd/mirum-server/config.go index 854e7ec..48d6ed2 100644 --- a/cmd/mirumd/config.go +++ b/cmd/mirum-server/config.go @@ -35,7 +35,7 @@ func getConfig(filename string) (*appConfig, error) { cfg := &appConfig{ GrpcAddr: ":2026", WebAddr: ":3000", - AdminSocket: "/run/mirumd/admin.sock", + AdminSocket: "/run/mirum-server/admin.sock", } data, err := os.ReadFile(filename) diff --git a/cmd/mirumd/database.go b/cmd/mirum-server/database.go index 6c03f7a..8655dbc 100644 --- a/cmd/mirumd/database.go +++ b/cmd/mirum-server/database.go @@ -18,8 +18,8 @@ import ( "strings" "time" + "dimidiumlabs/mirum/cmd/mirum-server/apipb" "dimidiumlabs/mirum/internal/config" - "dimidiumlabs/mirum/internal/protocol/pb" sb "github.com/huandu/go-sqlbuilder" "github.com/jackc/pgerrcode" @@ -469,7 +469,7 @@ func (db *DB) UserGet(ctx context.Context, actor Actor, ref UserRef) (*User, err var u User err := db.apicall( ctx, actor, - func(tx pgx.Tx) error { return checkGlobal(actor, pb.Perm_PERM_USER_READ) }, + func(tx pgx.Tx) error { return checkGlobal(actor, apipb.Perm_PERM_USER_READ) }, nil, func(tx pgx.Tx) error { col, val := ref.where() @@ -498,7 +498,7 @@ func (db *DB) UserList(ctx context.Context, actor Actor, cursor UserID, limit in var users []User var total int err := db.apicall(ctx, actor, - func(tx pgx.Tx) error { return checkGlobal(actor, pb.Perm_PERM_USER_READ) }, + func(tx pgx.Tx) error { return checkGlobal(actor, apipb.Perm_PERM_USER_READ) }, func(tx pgx.Tx) error { if filter != "" { return ErrNotImplemented @@ -750,7 +750,7 @@ func (db *DB) UserSessionPurgeExpired(ctx context.Context) error { func (db *DB) OrgGet(ctx context.Context, actor Actor, ref OrgRef) (*Organization, error) { var o Organization err := db.apicall(ctx, actor, - func(tx pgx.Tx) error { return checkGlobal(actor, pb.Perm_PERM_ORG_READ) }, + func(tx pgx.Tx) error { return checkGlobal(actor, apipb.Perm_PERM_ORG_READ) }, nil, func(tx pgx.Tx) error { col, val := ref.where() @@ -777,7 +777,7 @@ func (db *DB) OrgGet(ctx context.Context, actor Actor, ref OrgRef) (*Organizatio func (db *DB) OrgCreate(ctx context.Context, actor Actor, name, slug string, public bool, owner UserRef) (OrgID, error) { var orgID OrgID err := db.apicall(ctx, actor, - func(tx pgx.Tx) error { return checkGlobal(actor, pb.Perm_PERM_ORG_WRITE) }, + func(tx pgx.Tx) error { return checkGlobal(actor, apipb.Perm_PERM_ORG_WRITE) }, nil, func(tx pgx.Tx) error { userID, err := resolveUser(ctx, tx, owner) @@ -820,7 +820,7 @@ func (db *DB) OrgUpdate(ctx context.Context, actor Actor, ref OrgRef, name *stri if err != nil { return err } - return checkPerm(ctx, tx, actor, id, pb.Perm_PERM_ORG_WRITE) + return checkPerm(ctx, tx, actor, id, apipb.Perm_PERM_ORG_WRITE) }, nil, func(tx pgx.Tx) error { @@ -864,7 +864,7 @@ func (db *DB) OrgDelete(ctx context.Context, actor Actor, ref OrgRef) error { if err != nil { return err } - return checkPerm(ctx, tx, actor, id, pb.Perm_PERM_ORG_DELETE) + return checkPerm(ctx, tx, actor, id, apipb.Perm_PERM_ORG_DELETE) }, nil, func(tx pgx.Tx) error { @@ -890,7 +890,7 @@ func (db *DB) OrgList(ctx context.Context, actor Actor, cursor OrgID, limit int, var orgs []Organization var total int err := db.apicall(ctx, actor, - func(tx pgx.Tx) error { return checkGlobal(actor, pb.Perm_PERM_ORG_READ) }, + func(tx pgx.Tx) error { return checkGlobal(actor, apipb.Perm_PERM_ORG_READ) }, func(tx pgx.Tx) error { if filter != "" { return ErrNotImplemented @@ -946,7 +946,7 @@ func (db *DB) OrgMemberGet(ctx context.Context, actor Actor, org OrgRef, user Us if err != nil { return err } - return checkPerm(ctx, tx, actor, orgID, pb.Perm_PERM_ORG_MEMBER_READ) + return checkPerm(ctx, tx, actor, orgID, apipb.Perm_PERM_ORG_MEMBER_READ) }, nil, func(tx pgx.Tx) error { @@ -985,7 +985,7 @@ func (db *DB) OrgMembersList(ctx context.Context, actor Actor, org OrgRef, curso if err != nil { return err } - return checkPerm(ctx, tx, actor, orgID, pb.Perm_PERM_ORG_MEMBER_READ) + return checkPerm(ctx, tx, actor, orgID, apipb.Perm_PERM_ORG_MEMBER_READ) }, func(tx pgx.Tx) error { if filter != "" { @@ -1041,7 +1041,7 @@ func (db *DB) OrgMemberAdd(ctx context.Context, actor Actor, org OrgRef, user Us if err != nil { return err } - return checkPerm(ctx, tx, actor, orgID, pb.Perm_PERM_ORG_MEMBER_WRITE) + return checkPerm(ctx, tx, actor, orgID, apipb.Perm_PERM_ORG_MEMBER_WRITE) }, nil, func(tx pgx.Tx) error { @@ -1075,7 +1075,7 @@ func (db *DB) OrgMemberUpdateRole(ctx context.Context, actor Actor, org OrgRef, if err != nil { return err } - return checkPerm(ctx, tx, actor, orgID, pb.Perm_PERM_ORG_MEMBER_WRITE) + return checkPerm(ctx, tx, actor, orgID, apipb.Perm_PERM_ORG_MEMBER_WRITE) }, nil, func(tx pgx.Tx) error { @@ -1128,7 +1128,7 @@ func (db *DB) OrgMemberRemove(ctx context.Context, actor Actor, org OrgRef, user if err != nil { return err } - return checkPerm(ctx, tx, actor, orgID, pb.Perm_PERM_ORG_MEMBER_WRITE) + return checkPerm(ctx, tx, actor, orgID, apipb.Perm_PERM_ORG_MEMBER_WRITE) }, nil, func(tx pgx.Tx) error { @@ -1185,7 +1185,7 @@ func (db *DB) WorkerGet(ctx context.Context, actor Actor, id WorkerID) (*Worker, return err } if orgID != nil { - return checkPerm(ctx, tx, actor, *orgID, pb.Perm_PERM_WORKER_READ) + return checkPerm(ctx, tx, actor, *orgID, apipb.Perm_PERM_WORKER_READ) } if !actor.IsSuperuser() { return ErrPermissionDenied @@ -1214,7 +1214,7 @@ func (db *DB) WorkerCreate(ctx context.Context, actor Actor, publicKey []byte, o return err } orgID = &id - return checkPerm(ctx, tx, actor, id, pb.Perm_PERM_WORKER_WRITE) + return checkPerm(ctx, tx, actor, id, apipb.Perm_PERM_WORKER_WRITE) } if !actor.IsSuperuser() { return ErrPermissionDenied @@ -1246,7 +1246,7 @@ func (db *DB) WorkerDelete(ctx context.Context, actor Actor, id WorkerID) error return err } if orgID != nil { - return checkPerm(ctx, tx, actor, *orgID, pb.Perm_PERM_WORKER_WRITE) + return checkPerm(ctx, tx, actor, *orgID, apipb.Perm_PERM_WORKER_WRITE) } if !actor.IsSuperuser() { return ErrPermissionDenied diff --git a/cmd/mirumd/id.go b/cmd/mirum-server/id.go index 09ec141..09ec141 100644 --- a/cmd/mirumd/id.go +++ b/cmd/mirum-server/id.go diff --git a/cmd/mirumd/id_test.go b/cmd/mirum-server/id_test.go index 0d06fc1..0d06fc1 100644 --- a/cmd/mirumd/id_test.go +++ b/cmd/mirum-server/id_test.go diff --git a/cmd/mirumd/main.go b/cmd/mirum-server/main.go index 73aa3f9..c592f71 100644 --- a/cmd/mirumd/main.go +++ b/cmd/mirum-server/main.go @@ -12,10 +12,10 @@ import ( "net/http" "os" + "dimidiumlabs/mirum/cmd/mirum-server/apipb/apipbconnect" "dimidiumlabs/mirum/internal/config" "dimidiumlabs/mirum/internal/forges" - "dimidiumlabs/mirum/internal/protocol/pb" - "dimidiumlabs/mirum/internal/protocol/pb/pbconnect" + "dimidiumlabs/mirum/internal/protocol/wirepb" "dimidiumlabs/mirum/internal/supervisor" "github.com/coreos/go-systemd/v22/activation" @@ -32,8 +32,8 @@ func hardenServer(s *http.Server) *http.Server { func main() { var socketPath string - root := &cobra.Command{Use: "mirumd", Short: "Mirum CI server"} - root.PersistentFlags().StringVar(&socketPath, "socket", "", "admin socket path (default from config or /run/mirumd/admin.sock)") + root := &cobra.Command{Use: "mirum-server", Short: "Mirum CI server"} + root.PersistentFlags().StringVar(&socketPath, "socket", "", "admin socket path (default from config or /run/mirum-server/admin.sock)") root.AddGroup(&cobra.Group{ID: "main", Title: "Commands:"}) daemonCmd := &cobra.Command{ @@ -51,8 +51,8 @@ func main() { _ = daemonCmd.MarkFlagRequired("config") root.AddCommand(daemonCmd) - // Admin subcommands are generated from admin.proto via reflection. - buildAdminCLI(root, func() pbconnect.AdminClient { return adminClient(socketPath) }) + // Console subcommands are generated from api.proto via reflection. + buildConsoleCLI(root, func() apipbconnect.ConsoleClient { return consoleClient(socketPath) }) for _, c := range root.Commands() { if c.GroupID == "" { c.GroupID = "main" @@ -91,7 +91,7 @@ func daemon(configFile, socketFlag string) error { db: db, cfg: cfg, forge: &forges.GitHub{Secret: cfg.WebhookSecret, Token: cfg.GitHubToken}, - queue: make(chan *pb.Task, config.TaskQueueCapacity), + queue: make(chan *wirepb.Task, config.TaskQueueCapacity), } defer srv.Close() @@ -104,13 +104,13 @@ func daemon(configFile, socketFlag string) error { go srv.PurgeSessions(ctx) - adminPath, adminHandler := NewAdminHandler(srv) + consolePath, consoleHandler := NewConsoleHandler(srv) - webSrv := hardenServer(NewWebServer(ctx, srv, adminPath, adminHandler)) + webSrv := hardenServer(NewWebServer(ctx, srv, consolePath, consoleHandler)) grpcSrv := hardenServer(NewGrpcServer(ctx, srv)) adminMux := http.NewServeMux() - adminMux.Handle(adminPath, adminHandler) + adminMux.Handle(consolePath, consoleHandler) adminSrv := hardenServer(&http.Server{ Handler: adminMux, ConnContext: func(ctx context.Context, _ net.Conn) context.Context { @@ -237,11 +237,11 @@ func listeners(cfg *appConfig) (grpcLn, webLn, adminLn net.Listener, err error) return grpcLn, webLn, adminLn, nil } -func adminClient(socketPath string) pbconnect.AdminClient { +func consoleClient(socketPath string) apipbconnect.ConsoleClient { if socketPath == "" { - socketPath = "/run/mirumd/admin.sock" + socketPath = "/run/mirum-server/admin.sock" } - return pbconnect.NewAdminClient( + return apipbconnect.NewConsoleClient( &http.Client{ Transport: &http.Transport{ DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { diff --git a/proto/admin.proto b/cmd/mirum-server/proto/api.proto index 58f4e64..16a635e 100644 --- a/proto/admin.proto +++ b/cmd/mirum-server/proto/api.proto @@ -3,14 +3,14 @@ syntax = "proto3"; -package mirum; +package mirum.api; import "buf/validate/validate.proto"; import "google/protobuf/timestamp.proto"; -option go_package = "dimidiumlabs/mirum/internal/protocol/pb"; +option go_package = "dimidiumlabs/mirum/cmd/mirum-server/apipb"; -service Admin { +service Console { rpc UserCreate(UserCreateRequest) returns (UserCreateResponse); rpc UserGet(UserGetRequest) returns (UserGetResponse); rpc UserList(UserListRequest) returns (UserListResponse); diff --git a/proto/buf.gen.yaml b/cmd/mirum-server/proto/buf.gen.yaml index 0f24cdc..a9a94e2 100644 --- a/proto/buf.gen.yaml +++ b/cmd/mirum-server/proto/buf.gen.yaml @@ -4,13 +4,13 @@ version: v2 plugins: - local: protoc-gen-go - out: ../internal/protocol/pb + out: ../apipb opt: paths=source_relative - local: protoc-gen-connect-go - out: ../internal/protocol/pb + out: ../apipb opt: paths=source_relative - remote: buf.build/bufbuild/es:v2.11.0 - out: ../cmd/mirumd/web/gen + out: ../web/gen opt: - target=ts - import_extension=js diff --git a/cmd/mirumd/server.go b/cmd/mirum-server/server.go index 11585d9..5b4bbf0 100644 --- a/cmd/mirumd/server.go +++ b/cmd/mirum-server/server.go @@ -13,7 +13,7 @@ import ( "dimidiumlabs/mirum/internal/config" "dimidiumlabs/mirum/internal/forges" - "dimidiumlabs/mirum/internal/protocol/pb" + "dimidiumlabs/mirum/internal/protocol/wirepb" ) // server holds the shared application state. @@ -22,7 +22,7 @@ type server struct { db *DB forge forges.Forge - queue chan *pb.Task + queue chan *wirepb.Task tasks sync.Map // task_id → *forges.PushEvent taskCounter atomic.Int64 } @@ -60,7 +60,7 @@ func (s *server) enqueue(ev *forges.PushEvent) string { s.tasks.Store(id, ev) _ = s.forge.SetStatus(context.Background(), ev, forges.StatusPending, "Queued") - s.queue <- &pb.Task{ + s.queue <- &wirepb.Task{ Id: id, CloneUrl: s.forge.AuthURL(ev.CloneURL), Branch: ev.Branch, diff --git a/cmd/mirumd/server_admin.go b/cmd/mirum-server/server_admin.go index 7e1dae1..a79c377 100644 --- a/cmd/mirumd/server_admin.go +++ b/cmd/mirum-server/server_admin.go @@ -13,21 +13,21 @@ import ( "connectrpc.com/validate" "google.golang.org/protobuf/types/known/timestamppb" - "dimidiumlabs/mirum/internal/protocol/pb" - "dimidiumlabs/mirum/internal/protocol/pb/pbconnect" + "dimidiumlabs/mirum/cmd/mirum-server/apipb" + "dimidiumlabs/mirum/cmd/mirum-server/apipb/apipbconnect" ) -// NewAdminHandler creates the ConnectRPC handler with validation. +// NewConsoleHandler creates the ConnectRPC handler with validation. // Authorization is handled inside DB methods, not by an interceptor. -func NewAdminHandler(srv *server) (string, http.Handler) { - as := &adminService{srv: srv} - return pbconnect.NewAdminHandler(as, +func NewConsoleHandler(srv *server) (string, http.Handler) { + as := &consoleService{srv: srv} + return apipbconnect.NewConsoleHandler(as, connect.WithInterceptors(validate.NewInterceptor()), ) } -type adminService struct { - pbconnect.UnimplementedAdminHandler +type consoleService struct { + apipbconnect.UnimplementedConsoleHandler srv *server } @@ -36,9 +36,9 @@ type adminService struct { // newAPIError builds a ConnectError with an empty message string and attaches // an ErrorInfo detail carrying the domain reason. Clients switch on reason to // pick user-facing text; the wire never carries human-readable strings. -func newAPIError(code connect.Code, reason pb.ErrorReason, metadata map[string]string) error { +func newAPIError(code connect.Code, reason apipb.ErrorReason, metadata map[string]string) error { e := connect.NewError(code, nil) - if d, err := connect.NewErrorDetail(&pb.ErrorInfo{Reason: reason, Metadata: metadata}); err == nil { + if d, err := connect.NewErrorDetail(&apipb.ErrorInfo{Reason: reason, Metadata: metadata}); err == nil { e.AddDetail(d) } return e @@ -47,23 +47,23 @@ func newAPIError(code connect.Code, reason pb.ErrorReason, metadata map[string]s var errSpecs = []struct { err error code connect.Code - reason pb.ErrorReason + reason apipb.ErrorReason }{ - {ErrUserNotFound, connect.CodeNotFound, pb.ErrorReason_ERROR_REASON_USER_NOT_FOUND}, - {ErrOrgNotFound, connect.CodeNotFound, pb.ErrorReason_ERROR_REASON_ORG_NOT_FOUND}, - {ErrWorkerNotFound, connect.CodeNotFound, pb.ErrorReason_ERROR_REASON_WORKER_NOT_FOUND}, - {ErrNotMember, connect.CodeNotFound, pb.ErrorReason_ERROR_REASON_MEMBER_NOT_FOUND}, - {ErrEmailTaken, connect.CodeAlreadyExists, pb.ErrorReason_ERROR_REASON_EMAIL_TAKEN}, - {ErrSlugTaken, connect.CodeAlreadyExists, pb.ErrorReason_ERROR_REASON_SLUG_TAKEN}, - {ErrAlreadyMember, connect.CodeAlreadyExists, pb.ErrorReason_ERROR_REASON_ALREADY_MEMBER}, - {ErrLastOwner, connect.CodeFailedPrecondition, pb.ErrorReason_ERROR_REASON_LAST_OWNER}, - {ErrSoleOwner, connect.CodeFailedPrecondition, pb.ErrorReason_ERROR_REASON_SOLE_OWNER}, - {ErrInvalidSlug, connect.CodeInvalidArgument, pb.ErrorReason_ERROR_REASON_INVALID_SLUG}, - {ErrInvalidRole, connect.CodeInvalidArgument, pb.ErrorReason_ERROR_REASON_INVALID_ROLE}, - {ErrReservedEmail, connect.CodeInvalidArgument, pb.ErrorReason_ERROR_REASON_RESERVED_EMAIL}, - {ErrPermissionDenied, connect.CodePermissionDenied, pb.ErrorReason_ERROR_REASON_PERMISSION_DENIED}, - {ErrUnauthenticated, connect.CodeUnauthenticated, pb.ErrorReason_ERROR_REASON_UNAUTHENTICATED}, - {ErrNotImplemented, connect.CodeUnimplemented, pb.ErrorReason_ERROR_REASON_UNIMPLEMENTED}, + {ErrUserNotFound, connect.CodeNotFound, apipb.ErrorReason_ERROR_REASON_USER_NOT_FOUND}, + {ErrOrgNotFound, connect.CodeNotFound, apipb.ErrorReason_ERROR_REASON_ORG_NOT_FOUND}, + {ErrWorkerNotFound, connect.CodeNotFound, apipb.ErrorReason_ERROR_REASON_WORKER_NOT_FOUND}, + {ErrNotMember, connect.CodeNotFound, apipb.ErrorReason_ERROR_REASON_MEMBER_NOT_FOUND}, + {ErrEmailTaken, connect.CodeAlreadyExists, apipb.ErrorReason_ERROR_REASON_EMAIL_TAKEN}, + {ErrSlugTaken, connect.CodeAlreadyExists, apipb.ErrorReason_ERROR_REASON_SLUG_TAKEN}, + {ErrAlreadyMember, connect.CodeAlreadyExists, apipb.ErrorReason_ERROR_REASON_ALREADY_MEMBER}, + {ErrLastOwner, connect.CodeFailedPrecondition, apipb.ErrorReason_ERROR_REASON_LAST_OWNER}, + {ErrSoleOwner, connect.CodeFailedPrecondition, apipb.ErrorReason_ERROR_REASON_SOLE_OWNER}, + {ErrInvalidSlug, connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_INVALID_SLUG}, + {ErrInvalidRole, connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_INVALID_ROLE}, + {ErrReservedEmail, connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_RESERVED_EMAIL}, + {ErrPermissionDenied, connect.CodePermissionDenied, apipb.ErrorReason_ERROR_REASON_PERMISSION_DENIED}, + {ErrUnauthenticated, connect.CodeUnauthenticated, apipb.ErrorReason_ERROR_REASON_UNAUTHENTICATED}, + {ErrNotImplemented, connect.CodeUnimplemented, apipb.ErrorReason_ERROR_REASON_UNIMPLEMENTED}, } func mapErr(err error) error { @@ -76,35 +76,35 @@ func mapErr(err error) error { } } slog.Error("unmapped handler error", "err", err) - return newAPIError(connect.CodeInternal, pb.ErrorReason_ERROR_REASON_INTERNAL, nil) + return newAPIError(connect.CodeInternal, apipb.ErrorReason_ERROR_REASON_INTERNAL, nil) } // --- Ref converters --- -func userRef(r *pb.UserRef) (UserRef, error) { +func userRef(r *apipb.UserRef) (UserRef, error) { switch v := r.GetRef().(type) { - case *pb.UserRef_Id: + case *apipb.UserRef_Id: id, err := IDFromBytes[UserKind](v.Id) if err != nil { return UserRef{}, err } return UserByID(id), nil - case *pb.UserRef_Email: + case *apipb.UserRef_Email: return UserByEmail(v.Email), nil default: return UserRef{}, nil } } -func orgRef(r *pb.OrgRef) (OrgRef, error) { +func orgRef(r *apipb.OrgRef) (OrgRef, error) { switch v := r.GetRef().(type) { - case *pb.OrgRef_Id: + case *apipb.OrgRef_Id: id, err := IDFromBytes[OrgKind](v.Id) if err != nil { return OrgRef{}, err } return OrgByID(id), nil - case *pb.OrgRef_Slug: + case *apipb.OrgRef_Slug: return OrgBySlug(v.Slug), nil default: return OrgRef{}, nil @@ -113,16 +113,16 @@ func orgRef(r *pb.OrgRef) (OrgRef, error) { // --- Role converters --- -var roleToString = map[pb.Role]string{ - pb.Role_ROLE_OWNER: "owner", - pb.Role_ROLE_ADMIN: "admin", - pb.Role_ROLE_MEMBER: "member", +var roleToString = map[apipb.Role]string{ + apipb.Role_ROLE_OWNER: "owner", + apipb.Role_ROLE_ADMIN: "admin", + apipb.Role_ROLE_MEMBER: "member", } -var roleToProto = map[string]pb.Role{ - "owner": pb.Role_ROLE_OWNER, - "admin": pb.Role_ROLE_ADMIN, - "member": pb.Role_ROLE_MEMBER, +var roleToProto = map[string]apipb.Role{ + "owner": apipb.Role_ROLE_OWNER, + "admin": apipb.Role_ROLE_ADMIN, + "member": apipb.Role_ROLE_MEMBER, } // --- Page helpers --- @@ -132,7 +132,7 @@ const ( maxPageSize = 200 ) -func pageParams[K IDKind](p *pb.PageRequest) (cursor ID[K], limit int, err error) { +func pageParams[K IDKind](p *apipb.PageRequest) (cursor ID[K], limit int, err error) { limit = defaultPageSize if p != nil { if p.PageSize > 0 && int(p.PageSize) < maxPageSize { @@ -150,8 +150,8 @@ func pageParams[K IDKind](p *pb.PageRequest) (cursor ID[K], limit int, err error return } -func pageResponse[K IDKind](items int, limit int, lastID ID[K], total int) *pb.PageResponse { - resp := &pb.PageResponse{TotalCount: int32(total)} +func pageResponse[K IDKind](items int, limit int, lastID ID[K], total int) *apipb.PageResponse { + resp := &apipb.PageResponse{TotalCount: int32(total)} if items == limit { resp.NextCursor = lastID.Bytes() } @@ -160,28 +160,28 @@ func pageResponse[K IDKind](items int, limit int, lastID ID[K], total int) *pb.P // --- Proto converters --- -func userToProto(u User) *pb.User { - return &pb.User{ +func userToProto(u User) *apipb.User { + return &apipb.User{ Id: u.ID.Bytes(), Email: u.Email, CreatedAt: timestamppb.New(u.CreatedAt), } } -func orgToProto(o Organization) *pb.Org { - return &pb.Org{ +func orgToProto(o Organization) *apipb.Org { + return &apipb.Org{ Id: o.ID.Bytes(), Name: o.Name, Slug: o.Slug, Public: o.Public, CreatedAt: timestamppb.New(o.CreatedAt), } } -func memberToProto(m OrgMember) *pb.OrgMemberInfo { - return &pb.OrgMemberInfo{ +func memberToProto(m OrgMember) *apipb.OrgMemberInfo { + return &apipb.OrgMemberInfo{ User: userToProto(m.User), Role: roleToProto[m.Role], JoinedAt: timestamppb.New(m.JoinedAt), } } -func workerToProto(w Worker) *pb.Worker { - pw := &pb.Worker{ +func workerToProto(w Worker) *apipb.Worker { + pw := &apipb.Worker{ Id: w.ID.Bytes(), PublicKey: w.PublicKey, CreatedAt: timestamppb.New(w.CreatedAt), } if w.OrgID != nil { @@ -192,15 +192,15 @@ func workerToProto(w Worker) *pb.Worker { // --- User handlers --- -func (a *adminService) UserCreate(ctx context.Context, req *connect.Request[pb.UserCreateRequest]) (*connect.Response[pb.UserCreateResponse], error) { +func (a *consoleService) UserCreate(ctx context.Context, req *connect.Request[apipb.UserCreateRequest]) (*connect.Response[apipb.UserCreateResponse], error) { id, err := a.srv.db.UserCreate(ctx, ActorFromContext(ctx), req.Msg.Email, req.Msg.Password, []byte(a.srv.cfg.Pepper)) if err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.UserCreateResponse{Id: id.Bytes()}), nil + return connect.NewResponse(&apipb.UserCreateResponse{Id: id.Bytes()}), nil } -func (a *adminService) UserGet(ctx context.Context, req *connect.Request[pb.UserGetRequest]) (*connect.Response[pb.UserGetResponse], error) { +func (a *consoleService) UserGet(ctx context.Context, req *connect.Request[apipb.UserGetRequest]) (*connect.Response[apipb.UserGetResponse], error) { ref, err := userRef(req.Msg.User) if err != nil { return nil, mapErr(err) @@ -209,10 +209,10 @@ func (a *adminService) UserGet(ctx context.Context, req *connect.Request[pb.User if err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.UserGetResponse{User: userToProto(*u)}), nil + return connect.NewResponse(&apipb.UserGetResponse{User: userToProto(*u)}), nil } -func (a *adminService) UserList(ctx context.Context, req *connect.Request[pb.UserListRequest]) (*connect.Response[pb.UserListResponse], error) { +func (a *consoleService) UserList(ctx context.Context, req *connect.Request[apipb.UserListRequest]) (*connect.Response[apipb.UserListResponse], error) { cursor, limit, err := pageParams[UserKind](req.Msg.Page) if err != nil { return nil, mapErr(err) @@ -227,7 +227,7 @@ func (a *adminService) UserList(ctx context.Context, req *connect.Request[pb.Use return nil, mapErr(err) } - out := make([]*pb.User, len(users)) + out := make([]*apipb.User, len(users)) for i := range users { out[i] = userToProto(users[i]) } @@ -237,13 +237,13 @@ func (a *adminService) UserList(ctx context.Context, req *connect.Request[pb.Use lastID = users[len(users)-1].ID } - return connect.NewResponse(&pb.UserListResponse{ + return connect.NewResponse(&apipb.UserListResponse{ Page: pageResponse(len(users), limit, lastID, total), Users: out, }), nil } -func (a *adminService) UserUpdate(ctx context.Context, req *connect.Request[pb.UserUpdateRequest]) (*connect.Response[pb.UserUpdateResponse], error) { +func (a *consoleService) UserUpdate(ctx context.Context, req *connect.Request[apipb.UserUpdateRequest]) (*connect.Response[apipb.UserUpdateResponse], error) { ref, err := userRef(req.Msg.User) if err != nil { return nil, mapErr(err) @@ -251,10 +251,10 @@ func (a *adminService) UserUpdate(ctx context.Context, req *connect.Request[pb.U if err := a.srv.db.UserUpdate(ctx, ActorFromContext(ctx), ref, req.Msg.Email, req.Msg.Password, []byte(a.srv.cfg.Pepper)); err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.UserUpdateResponse{}), nil + return connect.NewResponse(&apipb.UserUpdateResponse{}), nil } -func (a *adminService) UserDelete(ctx context.Context, req *connect.Request[pb.UserDeleteRequest]) (*connect.Response[pb.UserDeleteResponse], error) { +func (a *consoleService) UserDelete(ctx context.Context, req *connect.Request[apipb.UserDeleteRequest]) (*connect.Response[apipb.UserDeleteResponse], error) { ref, err := userRef(req.Msg.User) if err != nil { return nil, mapErr(err) @@ -262,12 +262,12 @@ func (a *adminService) UserDelete(ctx context.Context, req *connect.Request[pb.U if err := a.srv.db.UserDelete(ctx, ActorFromContext(ctx), ref); err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.UserDeleteResponse{}), nil + return connect.NewResponse(&apipb.UserDeleteResponse{}), nil } // --- Org handlers --- -func (a *adminService) OrgCreate(ctx context.Context, req *connect.Request[pb.OrgCreateRequest]) (*connect.Response[pb.OrgCreateResponse], error) { +func (a *consoleService) OrgCreate(ctx context.Context, req *connect.Request[apipb.OrgCreateRequest]) (*connect.Response[apipb.OrgCreateResponse], error) { slug, err := ValidateSlug(req.Msg.Slug) if err != nil { return nil, mapErr(err) @@ -280,10 +280,10 @@ func (a *adminService) OrgCreate(ctx context.Context, req *connect.Request[pb.Or if err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.OrgCreateResponse{Id: id.Bytes()}), nil + return connect.NewResponse(&apipb.OrgCreateResponse{Id: id.Bytes()}), nil } -func (a *adminService) OrgGet(ctx context.Context, req *connect.Request[pb.OrgGetRequest]) (*connect.Response[pb.OrgGetResponse], error) { +func (a *consoleService) OrgGet(ctx context.Context, req *connect.Request[apipb.OrgGetRequest]) (*connect.Response[apipb.OrgGetResponse], error) { ref, err := orgRef(req.Msg.Org) if err != nil { return nil, mapErr(err) @@ -292,10 +292,10 @@ func (a *adminService) OrgGet(ctx context.Context, req *connect.Request[pb.OrgGe if err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.OrgGetResponse{Org: orgToProto(*o)}), nil + return connect.NewResponse(&apipb.OrgGetResponse{Org: orgToProto(*o)}), nil } -func (a *adminService) OrgList(ctx context.Context, req *connect.Request[pb.OrgListRequest]) (*connect.Response[pb.OrgListResponse], error) { +func (a *consoleService) OrgList(ctx context.Context, req *connect.Request[apipb.OrgListRequest]) (*connect.Response[apipb.OrgListResponse], error) { cursor, limit, err := pageParams[OrgKind](req.Msg.Page) if err != nil { return nil, mapErr(err) @@ -310,7 +310,7 @@ func (a *adminService) OrgList(ctx context.Context, req *connect.Request[pb.OrgL return nil, mapErr(err) } - out := make([]*pb.Org, len(orgs)) + out := make([]*apipb.Org, len(orgs)) for i := range orgs { out[i] = orgToProto(orgs[i]) } @@ -320,13 +320,13 @@ func (a *adminService) OrgList(ctx context.Context, req *connect.Request[pb.OrgL lastID = orgs[len(orgs)-1].ID } - return connect.NewResponse(&pb.OrgListResponse{ + return connect.NewResponse(&apipb.OrgListResponse{ Page: pageResponse(len(orgs), limit, lastID, total), Organizations: out, }), nil } -func (a *adminService) OrgUpdate(ctx context.Context, req *connect.Request[pb.OrgUpdateRequest]) (*connect.Response[pb.OrgUpdateResponse], error) { +func (a *consoleService) OrgUpdate(ctx context.Context, req *connect.Request[apipb.OrgUpdateRequest]) (*connect.Response[apipb.OrgUpdateResponse], error) { var slug *string if req.Msg.Slug != nil { s, err := ValidateSlug(*req.Msg.Slug) @@ -342,10 +342,10 @@ func (a *adminService) OrgUpdate(ctx context.Context, req *connect.Request[pb.Or if err := a.srv.db.OrgUpdate(ctx, ActorFromContext(ctx), ref, req.Msg.Name, slug, req.Msg.Public); err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.OrgUpdateResponse{}), nil + return connect.NewResponse(&apipb.OrgUpdateResponse{}), nil } -func (a *adminService) OrgDelete(ctx context.Context, req *connect.Request[pb.OrgDeleteRequest]) (*connect.Response[pb.OrgDeleteResponse], error) { +func (a *consoleService) OrgDelete(ctx context.Context, req *connect.Request[apipb.OrgDeleteRequest]) (*connect.Response[apipb.OrgDeleteResponse], error) { ref, err := orgRef(req.Msg.Org) if err != nil { return nil, mapErr(err) @@ -353,15 +353,15 @@ func (a *adminService) OrgDelete(ctx context.Context, req *connect.Request[pb.Or if err := a.srv.db.OrgDelete(ctx, ActorFromContext(ctx), ref); err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.OrgDeleteResponse{}), nil + return connect.NewResponse(&apipb.OrgDeleteResponse{}), nil } // --- OrgMember handlers --- -func (a *adminService) OrgMemberAdd(ctx context.Context, req *connect.Request[pb.OrgMemberAddRequest]) (*connect.Response[pb.OrgMemberAddResponse], error) { +func (a *consoleService) OrgMemberAdd(ctx context.Context, req *connect.Request[apipb.OrgMemberAddRequest]) (*connect.Response[apipb.OrgMemberAddResponse], error) { role, ok := roleToString[req.Msg.Role] if !ok { - return nil, newAPIError(connect.CodeInvalidArgument, pb.ErrorReason_ERROR_REASON_INVALID_ROLE, nil) + return nil, newAPIError(connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_INVALID_ROLE, nil) } org, err := orgRef(req.Msg.Org) if err != nil { @@ -374,10 +374,10 @@ func (a *adminService) OrgMemberAdd(ctx context.Context, req *connect.Request[pb if err := a.srv.db.OrgMemberAdd(ctx, ActorFromContext(ctx), org, user, role); err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.OrgMemberAddResponse{}), nil + return connect.NewResponse(&apipb.OrgMemberAddResponse{}), nil } -func (a *adminService) OrgMemberGet(ctx context.Context, req *connect.Request[pb.OrgMemberGetRequest]) (*connect.Response[pb.OrgMemberGetResponse], error) { +func (a *consoleService) OrgMemberGet(ctx context.Context, req *connect.Request[apipb.OrgMemberGetRequest]) (*connect.Response[apipb.OrgMemberGetResponse], error) { org, err := orgRef(req.Msg.Org) if err != nil { return nil, mapErr(err) @@ -390,10 +390,10 @@ func (a *adminService) OrgMemberGet(ctx context.Context, req *connect.Request[pb if err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.OrgMemberGetResponse{Member: memberToProto(*m)}), nil + return connect.NewResponse(&apipb.OrgMemberGetResponse{Member: memberToProto(*m)}), nil } -func (a *adminService) OrgMemberList(ctx context.Context, req *connect.Request[pb.OrgMemberListRequest]) (*connect.Response[pb.OrgMemberListResponse], error) { +func (a *consoleService) OrgMemberList(ctx context.Context, req *connect.Request[apipb.OrgMemberListRequest]) (*connect.Response[apipb.OrgMemberListResponse], error) { cursor, limit, err := pageParams[UserKind](req.Msg.Page) if err != nil { return nil, mapErr(err) @@ -412,7 +412,7 @@ func (a *adminService) OrgMemberList(ctx context.Context, req *connect.Request[p return nil, mapErr(err) } - out := make([]*pb.OrgMemberInfo, len(members)) + out := make([]*apipb.OrgMemberInfo, len(members)) for i := range members { out[i] = memberToProto(members[i]) } @@ -422,16 +422,16 @@ func (a *adminService) OrgMemberList(ctx context.Context, req *connect.Request[p lastID = members[len(members)-1].User.ID } - return connect.NewResponse(&pb.OrgMemberListResponse{ + return connect.NewResponse(&apipb.OrgMemberListResponse{ Page: pageResponse(len(members), limit, lastID, total), Members: out, }), nil } -func (a *adminService) OrgMemberUpdate(ctx context.Context, req *connect.Request[pb.OrgMemberUpdateRequest]) (*connect.Response[pb.OrgMemberUpdateResponse], error) { +func (a *consoleService) OrgMemberUpdate(ctx context.Context, req *connect.Request[apipb.OrgMemberUpdateRequest]) (*connect.Response[apipb.OrgMemberUpdateResponse], error) { role, ok := roleToString[req.Msg.Role] if !ok { - return nil, newAPIError(connect.CodeInvalidArgument, pb.ErrorReason_ERROR_REASON_INVALID_ROLE, nil) + return nil, newAPIError(connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_INVALID_ROLE, nil) } org, err := orgRef(req.Msg.Org) if err != nil { @@ -444,10 +444,10 @@ func (a *adminService) OrgMemberUpdate(ctx context.Context, req *connect.Request if err := a.srv.db.OrgMemberUpdateRole(ctx, ActorFromContext(ctx), org, user, role); err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.OrgMemberUpdateResponse{}), nil + return connect.NewResponse(&apipb.OrgMemberUpdateResponse{}), nil } -func (a *adminService) OrgMemberRemove(ctx context.Context, req *connect.Request[pb.OrgMemberRemoveRequest]) (*connect.Response[pb.OrgMemberRemoveResponse], error) { +func (a *consoleService) OrgMemberRemove(ctx context.Context, req *connect.Request[apipb.OrgMemberRemoveRequest]) (*connect.Response[apipb.OrgMemberRemoveResponse], error) { org, err := orgRef(req.Msg.Org) if err != nil { return nil, mapErr(err) @@ -459,12 +459,12 @@ func (a *adminService) OrgMemberRemove(ctx context.Context, req *connect.Request if err := a.srv.db.OrgMemberRemove(ctx, ActorFromContext(ctx), org, user); err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.OrgMemberRemoveResponse{}), nil + return connect.NewResponse(&apipb.OrgMemberRemoveResponse{}), nil } // --- Worker handlers --- -func (a *adminService) WorkerCreate(ctx context.Context, req *connect.Request[pb.WorkerCreateRequest]) (*connect.Response[pb.WorkerCreateResponse], error) { +func (a *consoleService) WorkerCreate(ctx context.Context, req *connect.Request[apipb.WorkerCreateRequest]) (*connect.Response[apipb.WorkerCreateResponse], error) { var org *OrgRef if req.Msg.Org != nil { r, err := orgRef(req.Msg.Org) @@ -477,10 +477,10 @@ func (a *adminService) WorkerCreate(ctx context.Context, req *connect.Request[pb if err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.WorkerCreateResponse{Id: id.Bytes()}), nil + return connect.NewResponse(&apipb.WorkerCreateResponse{Id: id.Bytes()}), nil } -func (a *adminService) WorkerGet(ctx context.Context, req *connect.Request[pb.WorkerGetRequest]) (*connect.Response[pb.WorkerGetResponse], error) { +func (a *consoleService) WorkerGet(ctx context.Context, req *connect.Request[apipb.WorkerGetRequest]) (*connect.Response[apipb.WorkerGetResponse], error) { wid, err := IDFromBytes[WorkerKind](req.Msg.Id) if err != nil { return nil, mapErr(err) @@ -489,10 +489,10 @@ func (a *adminService) WorkerGet(ctx context.Context, req *connect.Request[pb.Wo if err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.WorkerGetResponse{Worker: workerToProto(*w)}), nil + return connect.NewResponse(&apipb.WorkerGetResponse{Worker: workerToProto(*w)}), nil } -func (a *adminService) WorkerList(ctx context.Context, req *connect.Request[pb.WorkerListRequest]) (*connect.Response[pb.WorkerListResponse], error) { +func (a *consoleService) WorkerList(ctx context.Context, req *connect.Request[apipb.WorkerListRequest]) (*connect.Response[apipb.WorkerListResponse], error) { cursor, limit, err := pageParams[WorkerKind](req.Msg.Page) if err != nil { return nil, mapErr(err) @@ -507,7 +507,7 @@ func (a *adminService) WorkerList(ctx context.Context, req *connect.Request[pb.W return nil, mapErr(err) } - out := make([]*pb.Worker, len(workers)) + out := make([]*apipb.Worker, len(workers)) for i := range workers { out[i] = workerToProto(workers[i]) } @@ -517,13 +517,13 @@ func (a *adminService) WorkerList(ctx context.Context, req *connect.Request[pb.W lastID = workers[len(workers)-1].ID } - return connect.NewResponse(&pb.WorkerListResponse{ + return connect.NewResponse(&apipb.WorkerListResponse{ Page: pageResponse(len(workers), limit, lastID, total), Workers: out, }), nil } -func (a *adminService) WorkerDelete(ctx context.Context, req *connect.Request[pb.WorkerDeleteRequest]) (*connect.Response[pb.WorkerDeleteResponse], error) { +func (a *consoleService) WorkerDelete(ctx context.Context, req *connect.Request[apipb.WorkerDeleteRequest]) (*connect.Response[apipb.WorkerDeleteResponse], error) { wid, err := IDFromBytes[WorkerKind](req.Msg.Id) if err != nil { return nil, mapErr(err) @@ -531,5 +531,5 @@ func (a *adminService) WorkerDelete(ctx context.Context, req *connect.Request[pb if err := a.srv.db.WorkerDelete(ctx, ActorFromContext(ctx), wid); err != nil { return nil, mapErr(err) } - return connect.NewResponse(&pb.WorkerDeleteResponse{}), nil + return connect.NewResponse(&apipb.WorkerDeleteResponse{}), nil } diff --git a/cmd/mirumd/server_grpc.go b/cmd/mirum-server/server_grpc.go index a41604d..a2f7cf0 100644 --- a/cmd/mirumd/server_grpc.go +++ b/cmd/mirum-server/server_grpc.go @@ -20,14 +20,14 @@ import ( "dimidiumlabs/mirum/internal/config" "dimidiumlabs/mirum/internal/protocol" - "dimidiumlabs/mirum/internal/protocol/pb" - "dimidiumlabs/mirum/internal/protocol/pb/pbconnect" + "dimidiumlabs/mirum/internal/protocol/wirepb" + "dimidiumlabs/mirum/internal/protocol/wirepb/wirepbconnect" ) func NewGrpcServer(ctx context.Context, srv *server) *http.Server { gsrv := &grpcService{srv: srv} - path, handler := pbconnect.NewMirumHandler(gsrv, + path, handler := wirepbconnect.NewWorkerHandler(gsrv, connect.WithInterceptors(validate.NewInterceptor()), ) @@ -80,11 +80,11 @@ func NewGrpcServer(ctx context.Context, srv *server) *http.Server { // grpcService is the ConnectRPC transport adapter over server. type grpcService struct { - pbconnect.UnimplementedMirumHandler + wirepbconnect.UnimplementedWorkerHandler srv *server } -func (g *grpcService) Poll(ctx context.Context, req *connect.Request[pb.PollRequest]) (*connect.Response[pb.Task], error) { +func (g *grpcService) Poll(ctx context.Context, req *connect.Request[wirepb.PollRequest]) (*connect.Response[wirepb.Task], error) { select { case task, ok := <-g.srv.queue: if !ok { @@ -97,11 +97,11 @@ func (g *grpcService) Poll(ctx context.Context, req *connect.Request[pb.PollRequ } } -func (g *grpcService) Complete(ctx context.Context, req *connect.Request[pb.TaskResult]) (*connect.Response[pb.CompleteResponse], error) { +func (g *grpcService) Complete(ctx context.Context, req *connect.Request[wirepb.TaskResult]) (*connect.Response[wirepb.CompleteResponse], error) { if err := g.srv.complete(ctx, req.Msg.TaskId, req.Msg.Success, req.Msg.Error); err != nil { return nil, err } - return connect.NewResponse(&pb.CompleteResponse{}), nil + return connect.NewResponse(&wirepb.CompleteResponse{}), nil } // workerLog logs worker metadata from the mTLS client certificate diff --git a/cmd/mirumd/server_web.go b/cmd/mirum-server/server_web.go index 13c5154..8e62e53 100644 --- a/cmd/mirumd/server_web.go +++ b/cmd/mirum-server/server_web.go @@ -21,9 +21,9 @@ import ( "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/httprate" + "dimidiumlabs/mirum/cmd/mirum-server/apipb" "dimidiumlabs/mirum/internal/config" "dimidiumlabs/mirum/internal/forges" - "dimidiumlabs/mirum/internal/protocol/pb" ) // __Host- prefixed cookies can only be set with Secure, Path=/, and no @@ -34,7 +34,7 @@ const ( csrfCookie = "__Host-csrf" ) -func NewWebServer(ctx context.Context, srv *server, adminPath string, adminHandler http.Handler) *http.Server { +func NewWebServer(ctx context.Context, srv *server, consolePath string, consoleHandler http.Handler) *http.Server { h := &webHandler{ srv: srv, assets: newAssetResolver(), @@ -90,7 +90,7 @@ func NewWebServer(ctx context.Context, srv *server, adminPath string, adminHandl }) r.With(middleware.NoCache, httprate.LimitByIP(config.APIRateLimit, config.APIRateWindow)). - Mount("/api/v1", http.StripPrefix("/api/v1", adminHandler)) + Mount("/api/v1", http.StripPrefix("/api/v1", consoleHandler)) r.NotFound(func(w http.ResponseWriter, r *http.Request) { h.renderError(w, r, http.StatusNotFound) @@ -221,15 +221,15 @@ func (h *webHandler) webhook(w http.ResponseWriter, r *http.Request) { } func (h *webHandler) loginPage(w http.ResponseWriter, r *http.Request) { - h.renderLogin(w, r, http.StatusOK, pb.ErrorReason_ERROR_REASON_UNSPECIFIED) + h.renderLogin(w, r, http.StatusOK, apipb.ErrorReason_ERROR_REASON_UNSPECIFIED) } // renderLogin is the single entry point for every login-flow outcome that // lands back on the login page. Reason == UNSPECIFIED means no error banner. // No caller writes error text itself — the client maps reason → copy. -func (h *webHandler) renderLogin(w http.ResponseWriter, r *http.Request, status int, reason pb.ErrorReason) { +func (h *webHandler) renderLogin(w http.ResponseWriter, r *http.Request, status int, reason apipb.ErrorReason) { data := map[string]any{"csrf": csrfToken(w, r)} - if reason != pb.ErrorReason_ERROR_REASON_UNSPECIFIED { + if reason != apipb.ErrorReason_ERROR_REASON_UNSPECIFIED { data["errorReason"] = int32(reason) } h.assets.renderPage(w, "login", status, data) @@ -238,7 +238,7 @@ func (h *webHandler) renderLogin(w http.ResponseWriter, r *http.Request, status func (h *webHandler) login(w http.ResponseWriter, r *http.Request) { if !csrfOK(r) { clearCookie(w, csrfCookie) - h.renderLogin(w, r, http.StatusForbidden, pb.ErrorReason_ERROR_REASON_INVALID_CSRF) + h.renderLogin(w, r, http.StatusForbidden, apipb.ErrorReason_ERROR_REASON_INVALID_CSRF) return } @@ -247,14 +247,14 @@ func (h *webHandler) login(w http.ResponseWriter, r *http.Request) { userID, err := h.srv.db.UserVerifyPassword(r.Context(), SystemActor(), email, password, []byte(h.srv.cfg.Pepper)) if err != nil { - h.renderLogin(w, r, http.StatusUnauthorized, pb.ErrorReason_ERROR_REASON_INVALID_CREDENTIALS) + h.renderLogin(w, r, http.StatusUnauthorized, apipb.ErrorReason_ERROR_REASON_INVALID_CREDENTIALS) return } token, err := h.srv.db.UserSessionCreate(r.Context(), SystemActor(), userID) if err != nil { slog.Error("create session failed", "err", err) - h.renderLogin(w, r, http.StatusInternalServerError, pb.ErrorReason_ERROR_REASON_INTERNAL) + h.renderLogin(w, r, http.StatusInternalServerError, apipb.ErrorReason_ERROR_REASON_INTERNAL) return } diff --git a/cmd/mirumd/static.go b/cmd/mirum-server/static.go index bc304ba..bc304ba 100644 --- a/cmd/mirumd/static.go +++ b/cmd/mirum-server/static.go diff --git a/cmd/mirumd/static_dev.go b/cmd/mirum-server/static_dev.go index 2f6d559..2f6d559 100644 --- a/cmd/mirumd/static_dev.go +++ b/cmd/mirum-server/static_dev.go diff --git a/cmd/mirumd/static_prod.go b/cmd/mirum-server/static_prod.go index 891b04e..891b04e 100644 --- a/cmd/mirumd/static_prod.go +++ b/cmd/mirum-server/static_prod.go diff --git a/cmd/mirumd/web/api/client.ts b/cmd/mirum-server/web/api/client.ts index 5d0ebc3..669fd60 100644 --- a/cmd/mirumd/web/api/client.ts +++ b/cmd/mirum-server/web/api/client.ts @@ -3,7 +3,7 @@ import { createClient, type Interceptor } from "@connectrpc/connect" import { createConnectTransport } from "@connectrpc/connect-web" -import { Admin, ErrorReason } from "@/gen/admin_pb" +import { Console, ErrorReason } from "@/gen/api_pb" import { errorReason } from "@/lib/errors" const csrfInterceptor = (csrfToken: string): Interceptor => @@ -25,10 +25,10 @@ const authInterceptor: Interceptor = (next) => async (req) => { } } -export function createAdminClient(csrfToken: string) { +export function createConsoleClient(csrfToken: string) { const transport = createConnectTransport({ baseUrl: "/api/v1", interceptors: [csrfInterceptor(csrfToken), authInterceptor], }) - return createClient(Admin, transport) + return createClient(Console, transport) } diff --git a/cmd/mirumd/web/components.json b/cmd/mirum-server/web/components.json index a2f7d0b..a2f7d0b 100644 --- a/cmd/mirumd/web/components.json +++ b/cmd/mirum-server/web/components.json diff --git a/cmd/mirumd/web/components/pages/dashboard.tsx b/cmd/mirum-server/web/components/pages/dashboard.tsx index 51c2f83..dcfd8ba 100644 --- a/cmd/mirumd/web/components/pages/dashboard.tsx +++ b/cmd/mirum-server/web/components/pages/dashboard.tsx @@ -2,9 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-or-later import { useEffect, useMemo, useState } from "react" -import { createAdminClient } from "@/api/client" +import { createConsoleClient } from "@/api/client" import { formatError } from "@/lib/errors" -import type { Org } from "@/gen/admin_pb" +import type { Org } from "@/gen/api_pb" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" @@ -14,7 +14,7 @@ export type DashboardProps = { } export function Page({ user, csrf }: DashboardProps) { - const client = useMemo(() => createAdminClient(csrf), [csrf]) + const client = useMemo(() => createConsoleClient(csrf), [csrf]) const [orgs, setOrgs] = useState<Org[] | null>(null) const [error, setError] = useState<string | null>(null) diff --git a/cmd/mirumd/web/components/pages/error.tsx b/cmd/mirum-server/web/components/pages/error.tsx index 9184ed2..9184ed2 100644 --- a/cmd/mirumd/web/components/pages/error.tsx +++ b/cmd/mirum-server/web/components/pages/error.tsx diff --git a/cmd/mirumd/web/components/pages/login.tsx b/cmd/mirum-server/web/components/pages/login.tsx index 71d07db..4e85144 100644 --- a/cmd/mirumd/web/components/pages/login.tsx +++ b/cmd/mirum-server/web/components/pages/login.tsx @@ -7,7 +7,7 @@ import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field" -import { ErrorReason } from "@/gen/admin_pb" +import { ErrorReason } from "@/gen/api_pb" import { textForReason } from "@/lib/errors" export type LoginFormProps = { diff --git a/cmd/mirumd/web/components/ui/alert.tsx b/cmd/mirum-server/web/components/ui/alert.tsx index 746962e..746962e 100644 --- a/cmd/mirumd/web/components/ui/alert.tsx +++ b/cmd/mirum-server/web/components/ui/alert.tsx diff --git a/cmd/mirumd/web/components/ui/button.tsx b/cmd/mirum-server/web/components/ui/button.tsx index ca6d679..ca6d679 100644 --- a/cmd/mirumd/web/components/ui/button.tsx +++ b/cmd/mirum-server/web/components/ui/button.tsx diff --git a/cmd/mirumd/web/components/ui/card.tsx b/cmd/mirum-server/web/components/ui/card.tsx index 9859e5f..9859e5f 100644 --- a/cmd/mirumd/web/components/ui/card.tsx +++ b/cmd/mirum-server/web/components/ui/card.tsx diff --git a/cmd/mirumd/web/components/ui/field.tsx b/cmd/mirum-server/web/components/ui/field.tsx index 9517f55..9517f55 100644 --- a/cmd/mirumd/web/components/ui/field.tsx +++ b/cmd/mirum-server/web/components/ui/field.tsx diff --git a/cmd/mirumd/web/components/ui/input.tsx b/cmd/mirum-server/web/components/ui/input.tsx index 2bd6cad..2bd6cad 100644 --- a/cmd/mirumd/web/components/ui/input.tsx +++ b/cmd/mirum-server/web/components/ui/input.tsx diff --git a/cmd/mirumd/web/components/ui/label.tsx b/cmd/mirum-server/web/components/ui/label.tsx index 48d4304..48d4304 100644 --- a/cmd/mirumd/web/components/ui/label.tsx +++ b/cmd/mirum-server/web/components/ui/label.tsx diff --git a/cmd/mirumd/web/components/ui/separator.tsx b/cmd/mirum-server/web/components/ui/separator.tsx index 3c2a347..3c2a347 100644 --- a/cmd/mirumd/web/components/ui/separator.tsx +++ b/cmd/mirum-server/web/components/ui/separator.tsx diff --git a/cmd/mirumd/web/entries/dashboard.tsx b/cmd/mirum-server/web/entries/dashboard.tsx index 0921015..0921015 100644 --- a/cmd/mirumd/web/entries/dashboard.tsx +++ b/cmd/mirum-server/web/entries/dashboard.tsx diff --git a/cmd/mirumd/web/entries/error.tsx b/cmd/mirum-server/web/entries/error.tsx index 10b509e..10b509e 100644 --- a/cmd/mirumd/web/entries/error.tsx +++ b/cmd/mirum-server/web/entries/error.tsx diff --git a/cmd/mirumd/web/entries/login.tsx b/cmd/mirum-server/web/entries/login.tsx index 36428f9..36428f9 100644 --- a/cmd/mirumd/web/entries/login.tsx +++ b/cmd/mirum-server/web/entries/login.tsx diff --git a/cmd/mirumd/web/index.css b/cmd/mirum-server/web/index.css index 1c06db4..1c06db4 100644 --- a/cmd/mirumd/web/index.css +++ b/cmd/mirum-server/web/index.css diff --git a/cmd/mirumd/web/lib/errors.ts b/cmd/mirum-server/web/lib/errors.ts index cf8a20b..2244d1b 100644 --- a/cmd/mirumd/web/lib/errors.ts +++ b/cmd/mirum-server/web/lib/errors.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later import { Code, ConnectError } from "@connectrpc/connect" -import { ErrorInfoSchema, ErrorReason } from "@/gen/admin_pb" +import { ErrorInfoSchema, ErrorReason } from "@/gen/api_pb" // errorReason extracts the ErrorInfo.reason attached by the server. // Returns null for transport failures or non-mirum responses. diff --git a/cmd/mirumd/web/lib/mount.tsx b/cmd/mirum-server/web/lib/mount.tsx index 2c026ab..2c026ab 100644 --- a/cmd/mirumd/web/lib/mount.tsx +++ b/cmd/mirum-server/web/lib/mount.tsx diff --git a/cmd/mirumd/web/lib/utils.ts b/cmd/mirum-server/web/lib/utils.ts index 5d8fa3d..5d8fa3d 100644 --- a/cmd/mirumd/web/lib/utils.ts +++ b/cmd/mirum-server/web/lib/utils.ts diff --git a/cmd/mirumd/web/package-lock.json b/cmd/mirum-server/web/package-lock.json index aeb900c..aeb900c 100644 --- a/cmd/mirumd/web/package-lock.json +++ b/cmd/mirum-server/web/package-lock.json diff --git a/cmd/mirumd/web/package.json b/cmd/mirum-server/web/package.json index ce805ca..ce805ca 100644 --- a/cmd/mirumd/web/package.json +++ b/cmd/mirum-server/web/package.json diff --git a/cmd/mirumd/web/shell.html b/cmd/mirum-server/web/shell.html index ce74041..ce74041 100644 --- a/cmd/mirumd/web/shell.html +++ b/cmd/mirum-server/web/shell.html diff --git a/cmd/mirumd/web/tsconfig.json b/cmd/mirum-server/web/tsconfig.json index 90b2f46..90b2f46 100644 --- a/cmd/mirumd/web/tsconfig.json +++ b/cmd/mirum-server/web/tsconfig.json diff --git a/cmd/mirumd/web/vite.config.ts b/cmd/mirum-server/web/vite.config.ts index 6de11ff..6de11ff 100644 --- a/cmd/mirumd/web/vite.config.ts +++ b/cmd/mirum-server/web/vite.config.ts diff --git a/cmd/mirumw/client.go b/cmd/mirum-worker/client.go index 7f3886d..1525904 100644 --- a/cmd/mirumw/client.go +++ b/cmd/mirum-worker/client.go @@ -17,14 +17,14 @@ import ( "dimidiumlabs/mirum/internal/executor" "dimidiumlabs/mirum/internal/protocol" - "dimidiumlabs/mirum/internal/protocol/pb" - "dimidiumlabs/mirum/internal/protocol/pb/pbconnect" + "dimidiumlabs/mirum/internal/protocol/wirepb" + "dimidiumlabs/mirum/internal/protocol/wirepb/wirepbconnect" ) type client struct { cfg *config http *http.Client - handle pbconnect.MirumClient + handle wirepbconnect.WorkerClient } func dial(ctx context.Context, cfg *config) (*client, error) { @@ -75,7 +75,7 @@ func dial(ctx context.Context, cfg *config) (*client, error) { Transport: &http.Transport{TLSClientConfig: tlsCfg, ForceAttemptHTTP2: true}, }, } - c.handle = pbconnect.NewMirumClient(c.http, "https://"+cfg.Server, connect.WithGRPC()) + c.handle = wirepbconnect.NewWorkerClient(c.http, "https://"+cfg.Server, connect.WithGRPC()) slog.Info("dialing", "server", cfg.Server) @@ -86,7 +86,7 @@ func (c *client) close() {} func (c *client) work(ctx context.Context) error { for ctx.Err() == nil { - resp, err := c.handle.Poll(ctx, connect.NewRequest(&pb.PollRequest{})) + resp, err := c.handle.Poll(ctx, connect.NewRequest(&wirepb.PollRequest{})) if err != nil { return fmt.Errorf("poll: %w", err) } @@ -100,7 +100,7 @@ func (c *client) work(ctx context.Context) error { execErr := executor.Run(task.CloneUrl, task.Branch) - result := &pb.TaskResult{TaskId: task.Id, Success: execErr == nil} + result := &wirepb.TaskResult{TaskId: task.Id, Success: execErr == nil} if execErr != nil { result.Error = execErr.Error() slog.Error("task failed", "id", task.Id, "err", execErr) diff --git a/cmd/mirumw/config.go b/cmd/mirum-worker/config.go index 50e82d3..366fdf4 100644 --- a/cmd/mirumw/config.go +++ b/cmd/mirum-worker/config.go @@ -11,7 +11,7 @@ import ( ) // Runtime type of this worker binary. Different worker types -// (mirumw-vm, mirumw-docker, etc.) will have different values. +// (mirum-worker-vm, mirum-worker-docker, etc.) will have different values. const workerRuntime = "host" type config struct { diff --git a/cmd/mirumw/main.go b/cmd/mirum-worker/main.go index e2132b5..e2132b5 100644 --- a/cmd/mirumw/main.go +++ b/cmd/mirum-worker/main.go diff --git a/internal/config/config.go b/internal/config/config.go index c9d0521..3d1256c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,7 +1,7 @@ // Copyright (c) 2026 Nikolay Govorov // SPDX-License-Identifier: AGPL-3.0-or-later -// Package config holds hard-coded tunables shared across mirumd and mirumw: +// Package config holds hard-coded tunables shared across mirum-server and mirum-worker: // timeouts, sizes, intervals, and limits that are not (yet) exposed through // the YAML user config. Grouping them here keeps magic numbers out of call // sites and gives a single place to audit defaults. diff --git a/internal/protocol/platform.go b/internal/protocol/platform.go index 638a664..d48da44 100644 --- a/internal/protocol/platform.go +++ b/internal/protocol/platform.go @@ -11,7 +11,7 @@ import ( "strings" mirum "dimidiumlabs/mirum" - "dimidiumlabs/mirum/internal/protocol/pb" + "dimidiumlabs/mirum/internal/protocol/wirepb" ) var ErrInvalidVersion = errors.New("invalid version string") @@ -22,53 +22,53 @@ var ( Patch uint32 ) -var osMap = map[string]pb.Os{ - "linux": pb.Os_OS_LINUX, - "darwin": pb.Os_OS_DARWIN, - "windows": pb.Os_OS_WINDOWS, - "freebsd": pb.Os_OS_FREEBSD, - "openbsd": pb.Os_OS_OPENBSD, - "netbsd": pb.Os_OS_NETBSD, - "dragonfly": pb.Os_OS_DRAGONFLY, - "illumos": pb.Os_OS_ILLUMOS, - "solaris": pb.Os_OS_SOLARIS, - "aix": pb.Os_OS_AIX, - "plan9": pb.Os_OS_PLAN9, - "android": pb.Os_OS_ANDROID, - "ios": pb.Os_OS_IOS, - "js": pb.Os_OS_JS, - "wasip1": pb.Os_OS_WASIP1, +var osMap = map[string]wirepb.Os{ + "linux": wirepb.Os_OS_LINUX, + "darwin": wirepb.Os_OS_DARWIN, + "windows": wirepb.Os_OS_WINDOWS, + "freebsd": wirepb.Os_OS_FREEBSD, + "openbsd": wirepb.Os_OS_OPENBSD, + "netbsd": wirepb.Os_OS_NETBSD, + "dragonfly": wirepb.Os_OS_DRAGONFLY, + "illumos": wirepb.Os_OS_ILLUMOS, + "solaris": wirepb.Os_OS_SOLARIS, + "aix": wirepb.Os_OS_AIX, + "plan9": wirepb.Os_OS_PLAN9, + "android": wirepb.Os_OS_ANDROID, + "ios": wirepb.Os_OS_IOS, + "js": wirepb.Os_OS_JS, + "wasip1": wirepb.Os_OS_WASIP1, } -var archMap = map[string]pb.Arch{ - "amd64": pb.Arch_ARCH_AMD64, - "arm64": pb.Arch_ARCH_ARM64, - "386": pb.Arch_ARCH_386, - "arm": pb.Arch_ARCH_ARM, - "riscv64": pb.Arch_ARCH_RISCV64, - "ppc64le": pb.Arch_ARCH_PPC64LE, - "ppc64": pb.Arch_ARCH_PPC64, - "s390x": pb.Arch_ARCH_S390X, - "mips64le": pb.Arch_ARCH_MIPS64LE, - "mips64": pb.Arch_ARCH_MIPS64, - "mipsle": pb.Arch_ARCH_MIPSLE, - "mips": pb.Arch_ARCH_MIPS, - "loong64": pb.Arch_ARCH_LOONG64, - "wasm": pb.Arch_ARCH_WASM, +var archMap = map[string]wirepb.Arch{ + "amd64": wirepb.Arch_ARCH_AMD64, + "arm64": wirepb.Arch_ARCH_ARM64, + "386": wirepb.Arch_ARCH_386, + "arm": wirepb.Arch_ARCH_ARM, + "riscv64": wirepb.Arch_ARCH_RISCV64, + "ppc64le": wirepb.Arch_ARCH_PPC64LE, + "ppc64": wirepb.Arch_ARCH_PPC64, + "s390x": wirepb.Arch_ARCH_S390X, + "mips64le": wirepb.Arch_ARCH_MIPS64LE, + "mips64": wirepb.Arch_ARCH_MIPS64, + "mipsle": wirepb.Arch_ARCH_MIPSLE, + "mips": wirepb.Arch_ARCH_MIPS, + "loong64": wirepb.Arch_ARCH_LOONG64, + "wasm": wirepb.Arch_ARCH_WASM, } -func DetectOs() pb.Os { +func DetectOs() wirepb.Os { if v, ok := osMap[runtime.GOOS]; ok { return v } - return pb.Os_OS_UNSPECIFIED + return wirepb.Os_OS_UNSPECIFIED } -func DetectArch() pb.Arch { +func DetectArch() wirepb.Arch { if v, ok := archMap[runtime.GOARCH]; ok { return v } - return pb.Arch_ARCH_UNSPECIFIED + return wirepb.Arch_ARCH_UNSPECIFIED } func init() { @@ -99,6 +99,6 @@ func VersionString() string { return fmt.Sprintf("%d.%d.%d", Major, Minor, Patch) } -func VersionProto() *pb.Version { - return &pb.Version{Major: Major, Minor: Minor, Patch: Patch} +func VersionProto() *wirepb.Version { + return &wirepb.Version{Major: Major, Minor: Minor, Patch: Patch} } diff --git a/internal/protocol/proto/buf.gen.yaml b/internal/protocol/proto/buf.gen.yaml new file mode 100644 --- /dev/null +++ b/internal/protocol/proto/buf.gen.yaml @@ -0,0 +1,11 @@ +# Copyright (c) 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +version: v2 +plugins: + - local: protoc-gen-go + out: ../wirepb + opt: paths=source_relative + - local: protoc-gen-connect-go + out: ../wirepb + opt: paths=source_relative diff --git a/proto/mirum.proto b/internal/protocol/proto/wire.proto index dd85e26..73668e7 100644 --- a/proto/mirum.proto +++ b/internal/protocol/proto/wire.proto @@ -3,9 +3,9 @@ syntax = "proto3"; -package mirum; +package mirum.wire; -option go_package = "dimidiumlabs/mirum/internal/protocol/pb"; +option go_package = "dimidiumlabs/mirum/internal/protocol/wirepb"; // Describes the contract between the worker and the server. // GRPC is the only contract between them, so to implement your own worker, @@ -17,7 +17,7 @@ option go_package = "dimidiumlabs/mirum/internal/protocol/pb"; // // Worker metadata (name, version, os, arch) and clock skew detection // are embedded in the certificate (URI SAN and NotBefore). -service Mirum { +service Worker { // When a worker has free resources, it requests a task from the server. // The call will block if the server currently has no tasks. rpc Poll(PollRequest) returns (Task); diff --git a/nfpm.yaml b/nfpm.yaml index f19f0a3..62181e7 100644 --- a/nfpm.yaml +++ b/nfpm.yaml @@ -18,55 +18,55 @@ contents: - src: ./README.md dst: /usr/share/doc/mirum/README.md - - src: build/mirumd - dst: /usr/local/bin/mirumd + - src: build/mirum-server + dst: /usr/local/bin/mirum-server file_info: mode: 0755 - - src: build/mirumw - dst: /usr/local/bin/mirumw + - src: build/mirum-worker + dst: /usr/local/bin/mirum-worker file_info: mode: 0755 - - src: pkg/mirumd.yaml - dst: /etc/mirum/mirumd.yaml + - src: pkg/server/config.yaml + dst: /etc/mirum/server/config.yaml type: config|noreplace file_info: mode: 0640 owner: root - group: mirumd + group: mirum-server - - src: pkg/mirumw-default.yaml - dst: /etc/mirum/mirumw-default.yaml + - src: pkg/worker/default.yaml + dst: /etc/mirum/worker/default.yaml type: config|noreplace file_info: mode: 0640 owner: root - group: mirumw + group: mirum-worker - - src: pkg/mirumd.service - dst: /usr/lib/systemd/system/mirumd.service + - src: pkg/mirum-server.service + dst: /usr/lib/systemd/system/mirum-server.service file_info: mode: 0644 - - src: pkg/mirumw@.service - dst: /usr/lib/systemd/system/mirumw@.service + - src: pkg/mirum-worker@.service + dst: /usr/lib/systemd/system/mirum-worker@.service file_info: mode: 0644 - - dst: /var/lib/mirumd + - dst: /var/lib/mirum-server type: dir file_info: mode: 0750 - owner: mirumd - group: mirumd + owner: mirum-server + group: mirum-server - - dst: /var/lib/mirumw + - dst: /var/lib/mirum-worker type: dir file_info: mode: 0750 - owner: mirumw - group: mirumw + owner: mirum-worker + group: mirum-worker scripts: diff --git a/pkg/mirumd.service b/pkg/mirum-server.service index ab46526..69e440f 100644 --- a/pkg/mirumd.service +++ b/pkg/mirum-server.service @@ -8,7 +8,7 @@ After=time-sync.target network-online.target remote-fs.target nss-lookup.target Wants=time-sync.target # Socket activation (optional): -# Create mirumd.socket with named file descriptors "web" and "grpc": +# Create mirum-server.socket with named file descriptors "web" and "grpc": # # [Socket] # ListenStream=0.0.0.0:3000 @@ -22,14 +22,14 @@ Wants=time-sync.target [Service] Type=notify -User=mirumd -Group=mirumd +User=mirum-server +Group=mirum-server Restart=always RestartSec=30 WatchdogSec=30 NotifyAccess=main -ExecPaths=/usr/local/bin/mirumd /usr/lib -ExecStart=/usr/local/bin/mirumd daemon --config=/etc/mirum/mirumd.yaml +ExecPaths=/usr/local/bin/mirum-server /usr/lib +ExecStart=/usr/local/bin/mirum-server daemon --config=/etc/mirum/server/config.yaml LimitCORE=infinity LimitNOFILE=500000 AmbientCapabilities=CAP_NET_BIND_SERVICE diff --git a/pkg/mirumw@.service b/pkg/mirum-worker@.service index cf7ddd5..dbbc5e0 100644 --- a/pkg/mirumw@.service +++ b/pkg/mirum-worker@.service @@ -9,14 +9,14 @@ Wants=time-sync.target [Service] Type=notify -User=mirumw -Group=mirumw +User=mirum-worker +Group=mirum-worker Restart=always RestartSec=30 WatchdogSec=30 NotifyAccess=main -ExecPaths=/usr/local/bin/mirumw -ExecStart=/usr/local/bin/mirumw --config=/etc/mirum/mirumw-%i.yaml +ExecPaths=/usr/local/bin/mirum-worker +ExecStart=/usr/local/bin/mirum-worker --config=/etc/mirum/worker/%i.yaml LimitCORE=infinity LimitNOFILE=500000 AmbientCapabilities= diff --git a/pkg/scripts/postinstall.sh b/pkg/scripts/postinstall.sh index 2c80442..3479c75 100644 --- a/pkg/scripts/postinstall.sh +++ b/pkg/scripts/postinstall.sh @@ -4,10 +4,10 @@ set -e -if [ -x "/bin/systemctl" ] && [ -d /run/systemd/system ] && [ -f /usr/lib/systemd/system/mirumd.service ]; then +if [ -x "/bin/systemctl" ] && [ -d /run/systemd/system ] && [ -f /usr/lib/systemd/system/mirum-server.service ]; then /bin/systemctl daemon-reload # Don't enable by default, don't know in advance whether it's a daemon or a worker - # /bin/systemctl enable mirumd - # /bin/systemctl enable mirumw + # /bin/systemctl enable mirum-server + # /bin/systemctl enable mirum-worker fi diff --git a/pkg/scripts/preinstall.sh b/pkg/scripts/preinstall.sh index d15fee2..15f9f40 100644 --- a/pkg/scripts/preinstall.sh +++ b/pkg/scripts/preinstall.sh @@ -4,7 +4,7 @@ set -e -for svc in mirumd mirumw; do +for svc in mirum-server mirum-worker; do if ! getent group $svc >/dev/null; then groupadd --system $svc fi diff --git a/pkg/scripts/preremove.sh b/pkg/scripts/preremove.sh index fdb71a8..1c92a81 100644 --- a/pkg/scripts/preremove.sh +++ b/pkg/scripts/preremove.sh @@ -5,9 +5,9 @@ set -e if [ -x "/bin/systemctl" ] && [ -d /run/systemd/system ]; then - /bin/systemctl stop mirumd.service || true - /bin/systemctl disable mirumd.service || true + /bin/systemctl stop mirum-server.service || true + /bin/systemctl disable mirum-server.service || true - /bin/systemctl stop 'mirumw@*' || true - /bin/systemctl disable mirumw@.service || true + /bin/systemctl stop 'mirum-worker@*' || true + /bin/systemctl disable mirum-worker@.service || true fi diff --git a/pkg/mirumd.yaml b/pkg/server/config.yaml index 4828d9b..6ea9881 100644 --- a/pkg/mirumd.yaml +++ b/pkg/server/config.yaml @@ -2,10 +2,10 @@ # SPDX-License-Identifier: AGPL-3.0-or-later # Ignored when the corresponding systemd socket activation fd is present. -# See mirumd.socket for details (FileDescriptorName=grpc / web). +# See mirum-server.socket for details (FileDescriptorName=grpc / web). grpc_addr: :2026 web_addr: :3000 -admin_socket: /run/mirumd/admin.sock +admin_socket: /run/mirum-server/admin.sock database_uri: "" webhook_secret: "" token: "" diff --git a/pkg/mirumw-default.yaml b/pkg/worker/default.yaml index 3fc2076..f982ba1 100644 --- a/pkg/mirumw-default.yaml +++ b/pkg/worker/default.yaml @@ -5,13 +5,13 @@ server: localhost:2026 # Ed25519 private key for worker authentication (PEM-encoded PKCS8). # Generate with: -# openssl genpkey -algorithm Ed25519 -out /etc/mirum/mirumw-default.key -# chmod 600 /etc/mirum/mirumw-default.key +# openssl genpkey -algorithm Ed25519 -out /etc/mirum/worker/default.key +# chmod 600 /etc/mirum/worker/default.key # # Register the public key on the daemon: -# PUBKEY=$(openssl pkey -in /etc/mirum/mirumw-default.key -pubout -outform der | base64 -w0) -# mirumd --socket /run/mirumd/admin.sock worker add --pubkey "$PUBKEY" -key_file: /etc/mirum/mirumw-default.key +# PUBKEY=$(openssl pkey -in /etc/mirum/worker/default.key -pubout -outform der | base64 -w0) +# mirum-server --socket /run/mirum-server/admin.sock worker create --pubkey "$PUBKEY" +key_file: /etc/mirum/worker/default.key # Custom CA certificate for self-signed/dev TLS. # Leave empty to use system trust store. |
