3 Commits

Author SHA1 Message Date
818c0c2e14 Add ReadPasswordBytes for crypto use cases
Returns []byte so callers can zeroize the buffer after use.
Refactors internals to share readRaw between both variants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 11:32:36 -07:00
b886f9d222 Add terminal package with secure ReadPassword helper
Provides echo-suppressed password prompting via golang.org/x/term for
CLI login commands. Added as a platform standard in engineering-standards.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 11:10:06 -07:00
ebe2079a83 Migrate module path from kyle/ to mc/ org
All import paths updated from git.wntrmute.dev/kyle/mcdsl to
git.wntrmute.dev/mc/mcdsl to match the Gitea organization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:03:45 -07:00
19 changed files with 89 additions and 27 deletions

View File

@@ -27,7 +27,7 @@ problem, usable independently or together.
### Module Path
```
git.wntrmute.dev/kyle/mcdsl
git.wntrmute.dev/mc/mcdsl
```
### Dependencies
@@ -39,7 +39,7 @@ git.wntrmute.dev/kyle/mcdsl
| `github.com/pelletier/go-toml/v2` | TOML config parsing |
| `google.golang.org/grpc` | gRPC server |
| `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
dependencies to the platform.

View File

@@ -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.
**Module path:** `git.wntrmute.dev/kyle/mcdsl`
**Module path:** `git.wntrmute.dev/mc/mcdsl`
## Build Commands
@@ -37,6 +37,7 @@ go test ./auth/ -run TestCacheExpiry
| `web` | Session cookies, auth middleware, template rendering for htmx UIs |
| `health` | REST and gRPC health check handlers |
| `archive` | tar.zst snapshots with SQLite-aware backup (VACUUM INTO) |
| `terminal` | Secure terminal input (echo-suppressed password prompts) |
## Import Pattern
@@ -44,11 +45,11 @@ Services import individual packages with aliased imports:
```go
import (
mcdslauth "git.wntrmute.dev/kyle/mcdsl/auth"
mcdsldb "git.wntrmute.dev/kyle/mcdsl/db"
mcdslconfig "git.wntrmute.dev/kyle/mcdsl/config"
"git.wntrmute.dev/kyle/mcdsl/httpserver"
"git.wntrmute.dev/kyle/mcdsl/grpcserver"
mcdslauth "git.wntrmute.dev/mc/mcdsl/auth"
mcdsldb "git.wntrmute.dev/mc/mcdsl/db"
mcdslconfig "git.wntrmute.dev/mc/mcdsl/config"
"git.wntrmute.dev/mc/mcdsl/httpserver"
"git.wntrmute.dev/mc/mcdsl/grpcserver"
)
```
@@ -63,6 +64,7 @@ db --> (modernc.org/sqlite)
grpcserver --> auth, config
health --> db
httpserver --> config
terminal --> (golang.org/x/term)
web --> auth, csrf
```

View File

@@ -91,11 +91,11 @@ Remaining mcat-specific code:
- `web/` — templates and static assets (unchanged)
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)
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.

View File

@@ -10,7 +10,7 @@ code first).
## 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 `Makefile` with standard targets (build, test, vet, lint, all)
- [ ] Create `.gitignore`

View File

@@ -25,7 +25,7 @@ it and provide only their service-specific logic.
## Module Path
```
git.wntrmute.dev/kyle/mcdsl
git.wntrmute.dev/mc/mcdsl
```
## Packages
@@ -46,10 +46,10 @@ git.wntrmute.dev/kyle/mcdsl
```go
import (
"git.wntrmute.dev/kyle/mcdsl/auth"
"git.wntrmute.dev/kyle/mcdsl/db"
"git.wntrmute.dev/kyle/mcdsl/config"
"git.wntrmute.dev/kyle/mcdsl/httpserver"
"git.wntrmute.dev/mc/mcdsl/auth"
"git.wntrmute.dev/mc/mcdsl/db"
"git.wntrmute.dev/mc/mcdsl/config"
"git.wntrmute.dev/mc/mcdsl/httpserver"
)
// Load config with standard sections + service-specific fields.

View File

@@ -20,7 +20,7 @@ import (
"github.com/klauspost/compress/zstd"
"git.wntrmute.dev/kyle/mcdsl/db"
"git.wntrmute.dev/mc/mcdsl/db"
)
// defaultExcludePatterns are always excluded from snapshots.

View File

@@ -7,7 +7,7 @@ import (
"path/filepath"
"testing"
"git.wntrmute.dev/kyle/mcdsl/db"
"git.wntrmute.dev/mc/mcdsl/db"
)
// setupServiceDir creates a realistic /srv/<service>/ directory.

View File

@@ -34,7 +34,7 @@ import (
"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

3
go.mod
View File

@@ -1,4 +1,4 @@
module git.wntrmute.dev/kyle/mcdsl
module git.wntrmute.dev/mc/mcdsl
go 1.25.7
@@ -18,6 +18,7 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/net v0.48.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
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/protobuf v1.36.10 // indirect

2
go.sum
View File

@@ -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.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
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/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=

View File

@@ -21,7 +21,7 @@ import (
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"git.wntrmute.dev/kyle/mcdsl/auth"
"git.wntrmute.dev/mc/mcdsl/auth"
)
// MethodMap classifies gRPC methods for access control.

View File

@@ -13,7 +13,7 @@ import (
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"git.wntrmute.dev/kyle/mcdsl/auth"
"git.wntrmute.dev/mc/mcdsl/auth"
)
func mockMCIAS(t *testing.T) *httptest.Server {

View File

@@ -10,7 +10,7 @@ import (
"google.golang.org/grpc"
"git.wntrmute.dev/kyle/mcdsl/db"
"git.wntrmute.dev/mc/mcdsl/db"
)
func TestHandlerHealthy(t *testing.T) {

View File

@@ -13,7 +13,7 @@ import (
"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

View File

@@ -9,7 +9,7 @@ import (
"testing"
"time"
"git.wntrmute.dev/kyle/mcdsl/config"
"git.wntrmute.dev/mc/mcdsl/config"
)
func testConfig() config.ServerConfig {

36
terminal/terminal.go Normal file
View 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
View 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")
}
}

View File

@@ -8,7 +8,7 @@ import (
"io/fs"
"net/http"
"git.wntrmute.dev/kyle/mcdsl/auth"
"git.wntrmute.dev/mc/mcdsl/auth"
)
// SetSessionCookie sets a session cookie with the standard Metacircular

View File

@@ -9,7 +9,7 @@ import (
"testing"
"testing/fstest"
"git.wntrmute.dev/kyle/mcdsl/auth"
"git.wntrmute.dev/mc/mcdsl/auth"
)
func TestSetSessionCookie(t *testing.T) {