Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 818c0c2e14 | |||
| b886f9d222 | |||
| ebe2079a83 |
@@ -27,7 +27,7 @@ problem, usable independently or together.
|
|||||||
### Module Path
|
### Module Path
|
||||||
|
|
||||||
```
|
```
|
||||||
git.wntrmute.dev/kyle/mcdsl
|
git.wntrmute.dev/mc/mcdsl
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
@@ -39,7 +39,7 @@ git.wntrmute.dev/kyle/mcdsl
|
|||||||
| `github.com/pelletier/go-toml/v2` | TOML config parsing |
|
| `github.com/pelletier/go-toml/v2` | TOML config parsing |
|
||||||
| `google.golang.org/grpc` | gRPC server |
|
| `google.golang.org/grpc` | gRPC server |
|
||||||
| `github.com/klauspost/compress/zstd` | Zstandard compression for archives |
|
| `github.com/klauspost/compress/zstd` | Zstandard compression for archives |
|
||||||
| `git.wntrmute.dev/kyle/mcias/clients/go` | MCIAS client library |
|
| `git.wntrmute.dev/mc/mcias/clients/go` | MCIAS client library |
|
||||||
|
|
||||||
All dependencies are already used by existing services. MCDSL adds no new
|
All dependencies are already used by existing services. MCDSL adds no new
|
||||||
dependencies to the platform.
|
dependencies to the platform.
|
||||||
|
|||||||
14
CLAUDE.md
14
CLAUDE.md
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
MCDSL (Metacircular Dynamics Standard Library) is the shared Go library for the Metacircular platform. It extracts common patterns from production services into reusable, tested packages. MCDSL is not a deployed service — it is imported by other services.
|
MCDSL (Metacircular Dynamics Standard Library) is the shared Go library for the Metacircular platform. It extracts common patterns from production services into reusable, tested packages. MCDSL is not a deployed service — it is imported by other services.
|
||||||
|
|
||||||
**Module path:** `git.wntrmute.dev/kyle/mcdsl`
|
**Module path:** `git.wntrmute.dev/mc/mcdsl`
|
||||||
|
|
||||||
## Build Commands
|
## Build Commands
|
||||||
|
|
||||||
@@ -37,6 +37,7 @@ go test ./auth/ -run TestCacheExpiry
|
|||||||
| `web` | Session cookies, auth middleware, template rendering for htmx UIs |
|
| `web` | Session cookies, auth middleware, template rendering for htmx UIs |
|
||||||
| `health` | REST and gRPC health check handlers |
|
| `health` | REST and gRPC health check handlers |
|
||||||
| `archive` | tar.zst snapshots with SQLite-aware backup (VACUUM INTO) |
|
| `archive` | tar.zst snapshots with SQLite-aware backup (VACUUM INTO) |
|
||||||
|
| `terminal` | Secure terminal input (echo-suppressed password prompts) |
|
||||||
|
|
||||||
## Import Pattern
|
## Import Pattern
|
||||||
|
|
||||||
@@ -44,11 +45,11 @@ Services import individual packages with aliased imports:
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
mcdslauth "git.wntrmute.dev/kyle/mcdsl/auth"
|
mcdslauth "git.wntrmute.dev/mc/mcdsl/auth"
|
||||||
mcdsldb "git.wntrmute.dev/kyle/mcdsl/db"
|
mcdsldb "git.wntrmute.dev/mc/mcdsl/db"
|
||||||
mcdslconfig "git.wntrmute.dev/kyle/mcdsl/config"
|
mcdslconfig "git.wntrmute.dev/mc/mcdsl/config"
|
||||||
"git.wntrmute.dev/kyle/mcdsl/httpserver"
|
"git.wntrmute.dev/mc/mcdsl/httpserver"
|
||||||
"git.wntrmute.dev/kyle/mcdsl/grpcserver"
|
"git.wntrmute.dev/mc/mcdsl/grpcserver"
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -63,6 +64,7 @@ db --> (modernc.org/sqlite)
|
|||||||
grpcserver --> auth, config
|
grpcserver --> auth, config
|
||||||
health --> db
|
health --> db
|
||||||
httpserver --> config
|
httpserver --> config
|
||||||
|
terminal --> (golang.org/x/term)
|
||||||
web --> auth, csrf
|
web --> auth, csrf
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -91,11 +91,11 @@ Remaining mcat-specific code:
|
|||||||
- `web/` — templates and static assets (unchanged)
|
- `web/` — templates and static assets (unchanged)
|
||||||
|
|
||||||
Dependencies removed:
|
Dependencies removed:
|
||||||
- `git.wntrmute.dev/kyle/mcias/clients/go` (mcdsl/auth handles MCIAS directly)
|
- `git.wntrmute.dev/mc/mcias/clients/go` (mcdsl/auth handles MCIAS directly)
|
||||||
- `github.com/pelletier/go-toml/v2` (now indirect via mcdsl/config)
|
- `github.com/pelletier/go-toml/v2` (now indirect via mcdsl/config)
|
||||||
|
|
||||||
Dependencies added:
|
Dependencies added:
|
||||||
- `git.wntrmute.dev/kyle/mcdsl` (local replace directive)
|
- `git.wntrmute.dev/mc/mcdsl` (local replace directive)
|
||||||
|
|
||||||
Result: vet clean, lint 0 issues, builds successfully.
|
Result: vet clean, lint 0 issues, builds successfully.
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ code first).
|
|||||||
|
|
||||||
## Phase 0: Project Setup
|
## Phase 0: Project Setup
|
||||||
|
|
||||||
- [ ] Initialize Go module (`git.wntrmute.dev/kyle/mcdsl`)
|
- [ ] Initialize Go module (`git.wntrmute.dev/mc/mcdsl`)
|
||||||
- [ ] Create `.golangci.yaml` (matching platform standard)
|
- [ ] Create `.golangci.yaml` (matching platform standard)
|
||||||
- [ ] Create `Makefile` with standard targets (build, test, vet, lint, all)
|
- [ ] Create `Makefile` with standard targets (build, test, vet, lint, all)
|
||||||
- [ ] Create `.gitignore`
|
- [ ] Create `.gitignore`
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -25,7 +25,7 @@ it and provide only their service-specific logic.
|
|||||||
## Module Path
|
## Module Path
|
||||||
|
|
||||||
```
|
```
|
||||||
git.wntrmute.dev/kyle/mcdsl
|
git.wntrmute.dev/mc/mcdsl
|
||||||
```
|
```
|
||||||
|
|
||||||
## Packages
|
## Packages
|
||||||
@@ -46,10 +46,10 @@ git.wntrmute.dev/kyle/mcdsl
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
"git.wntrmute.dev/kyle/mcdsl/auth"
|
"git.wntrmute.dev/mc/mcdsl/auth"
|
||||||
"git.wntrmute.dev/kyle/mcdsl/db"
|
"git.wntrmute.dev/mc/mcdsl/db"
|
||||||
"git.wntrmute.dev/kyle/mcdsl/config"
|
"git.wntrmute.dev/mc/mcdsl/config"
|
||||||
"git.wntrmute.dev/kyle/mcdsl/httpserver"
|
"git.wntrmute.dev/mc/mcdsl/httpserver"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load config with standard sections + service-specific fields.
|
// Load config with standard sections + service-specific fields.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
|
|
||||||
"github.com/klauspost/compress/zstd"
|
"github.com/klauspost/compress/zstd"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/db"
|
"git.wntrmute.dev/mc/mcdsl/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultExcludePatterns are always excluded from snapshots.
|
// defaultExcludePatterns are always excluded from snapshots.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/db"
|
"git.wntrmute.dev/mc/mcdsl/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
// setupServiceDir creates a realistic /srv/<service>/ directory.
|
// setupServiceDir creates a realistic /srv/<service>/ directory.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pelletier/go-toml/v2"
|
"github.com/pelletier/go-toml/v2"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/auth"
|
"git.wntrmute.dev/mc/mcdsl/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Base contains the configuration sections common to all Metacircular
|
// Base contains the configuration sections common to all Metacircular
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -1,4 +1,4 @@
|
|||||||
module git.wntrmute.dev/kyle/mcdsl
|
module git.wntrmute.dev/mc/mcdsl
|
||||||
|
|
||||||
go 1.25.7
|
go 1.25.7
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ require (
|
|||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
golang.org/x/net v0.48.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
|
golang.org/x/term v0.41.0 // indirect
|
||||||
golang.org/x/text v0.32.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -49,6 +49,8 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
|||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||||
|
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/auth"
|
"git.wntrmute.dev/mc/mcdsl/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MethodMap classifies gRPC methods for access control.
|
// MethodMap classifies gRPC methods for access control.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/auth"
|
"git.wntrmute.dev/mc/mcdsl/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mockMCIAS(t *testing.T) *httptest.Server {
|
func mockMCIAS(t *testing.T) *httptest.Server {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/db"
|
"git.wntrmute.dev/mc/mcdsl/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHandlerHealthy(t *testing.T) {
|
func TestHandlerHealthy(t *testing.T) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/config"
|
"git.wntrmute.dev/mc/mcdsl/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server wraps a chi router and an http.Server with the standard
|
// Server wraps a chi router and an http.Server with the standard
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/config"
|
"git.wntrmute.dev/mc/mcdsl/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testConfig() config.ServerConfig {
|
func testConfig() config.ServerConfig {
|
||||||
|
|||||||
36
terminal/terminal.go
Normal file
36
terminal/terminal.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// Package terminal provides secure terminal input helpers for CLI tools.
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadPassword prints the given prompt to stderr and reads a password
|
||||||
|
// from the terminal with echo disabled. It prints a newline after the
|
||||||
|
// input is complete so the cursor advances normally.
|
||||||
|
func ReadPassword(prompt string) (string, error) {
|
||||||
|
b, err := readRaw(prompt)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPasswordBytes is like ReadPassword but returns a []byte so the
|
||||||
|
// caller can zeroize the buffer after use.
|
||||||
|
func ReadPasswordBytes(prompt string) ([]byte, error) {
|
||||||
|
return readRaw(prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readRaw(prompt string) ([]byte, error) {
|
||||||
|
fmt.Fprint(os.Stderr, prompt)
|
||||||
|
b, err := term.ReadPassword(int(os.Stdin.Fd())) //nolint:gosec // fd fits in int
|
||||||
|
fmt.Fprintln(os.Stderr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
21
terminal/terminal_test.go
Normal file
21
terminal/terminal_test.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadPasswordNotATTY(t *testing.T) {
|
||||||
|
// When stdin is not a terminal (e.g. in CI), ReadPassword should
|
||||||
|
// return an error rather than hanging or panicking.
|
||||||
|
_, err := ReadPassword("Password: ")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error when stdin is not a terminal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadPasswordBytesNotATTY(t *testing.T) {
|
||||||
|
_, err := ReadPasswordBytes("Password: ")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error when stdin is not a terminal")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/auth"
|
"git.wntrmute.dev/mc/mcdsl/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetSessionCookie sets a session cookie with the standard Metacircular
|
// SetSessionCookie sets a session cookie with the standard Metacircular
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"testing/fstest"
|
"testing/fstest"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/mcdsl/auth"
|
"git.wntrmute.dev/mc/mcdsl/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSetSessionCookie(t *testing.T) {
|
func TestSetSessionCookie(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user