nullusclient

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package nullusclient 는 nullus CLI(트랙 A)와 MCP 서버(트랙 B)가 공유하는 유일한 기반이다 — Nullus API 클라이언트와 설정·토큰 해석.

두 트랙은 이 패키지에만 의존하고 서로에게 의존하지 않는다 (CLI+MCP 구현 백로그 Phase 1). 여기에 명령 표면이나 tool 표면을 넣지 않는다.

Index

Constants

View Source
const (
	EnvServer = "NULLUS_SERVER"
	EnvToken  = "NULLUS_TOKEN"
	// EnvConfigDir 은 ~/.nullus 를 통째로 옮긴다. 테스트 격리용이면서,
	// 멀티 서버 컨텍스트(v2)가 들어올 자리이기도 하다.
	EnvConfigDir = "NULLUS_CONFIG_DIR"
)

환경변수 이름. env 는 설정 파일보다 우선한다 — CI 에서 파일 없이 실행하기 위한 경로다 (Automation 계약 §5).

View Source
const MinServerVersion = "0.1.0-alpha"

MinServerVersion 은 이 클라이언트가 지원하는 최소 서버 버전이다 (백로그 S-4). 서버 API 에 breaking change 가 들어갈 때만 올린다 — 값의 출처는 cmd/api/main.go /health 의 version 필드.

Variables

View Source
var ErrLoginRequired = errors.New("로그인이 필요하다 — nullus login 을 실행하라")

ErrLoginRequired 는 재로그인 없이는 진행할 수 없는 상태다 — refresh token 만료·폐기, 또는 만료된 세션에 refresh token 이 없는 경우. 호출측은 errors.Is 로 구분해 `nullus login` 안내를 낸다 (exit code 3, 계약 §1).

Functions

func DeleteSession

func DeleteSession() error

DeleteSession 은 로컬 자격(세션 + 토큰 파일)을 지운다. logout(A-3)용 — 지울 것이 없어도 오류가 아니다(멱등).

func EnsureFreshToken

func EnsureFreshToken(ctx context.Context, hc *http.Client) (string, error)

EnsureFreshToken 은 지금 쓸 수 있는 access token 을 돌려준다. 우선순위는 S-2 와 같다: NULLUS_TOKEN env → 로그인 세션(만료 임박 시 refresh 후 영속) → 정적 토큰 파일(bootstrap) → 빈 값. 자격 부재는 오류가 아니다 — dev 모드(auth.mode=session)는 토큰 없이 동작한다.

func ReadToken

func ReadToken() (string, error)

ReadToken 은 env → 토큰 파일 순으로 토큰을 찾는다. 파일이 없으면 빈 문자열을 반환한다 — 토큰 부재는 로그인 안내의 재료이지 오류가 아니다.

func SaveSession

func SaveSession(s Session) error

SaveSession 은 세션을 ~/.nullus/session 에 0600 으로 저장하고, access token 을 S-2 토큰 파일에도 함께 기록한다 — ReadToken/Load 만 아는 소비자(MCP 설계 §5 의 단일 토큰 캐시 경로)가 로그인 결과를 그대로 보게 하기 위해서다.

func SaveToken

func SaveToken(token string) error

SaveToken 은 토큰을 ~/.nullus/token 에 0600 으로 저장한다. 디렉토리가 없으면 0700 으로 만든다. 이미 있던 파일도 권한을 0600 으로 되돌린다.

Types

type APIError

type APIError struct {
	Kind       Kind
	StatusCode int    // transport 실패면 0
	Code       string // 서버 error envelope 의 도메인 코드 (예: DEPLOY_COMPAT_WARN_UNACK)
	Message    string // envelope 의 message, 없으면 본문/원인 요약
	TraceID    string // 서버 request id — 지원 문의·로그 대조용
	// contains filtered or unexported fields
}

APIError 는 실패한 API 호출 하나를 설명한다. HTTP 응답 실패와 transport 실패(연결 불가 등) 모두 이 타입으로 돌아온다 — 일반 분기는 Kind 로 하고, 같은 상태 코드 안에서 갈라야 하는 경우(예: 400 중 DEPLOY_COMPAT_WARN_UNACK 를 --ack-warnings 안내로 바꾸는 A-5)만 Code 를 본다.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Unwrap

func (e *APIError) Unwrap() error

type AuthCodeFlow

type AuthCodeFlow struct {
	// contains filtered or unexported fields
}

AuthCodeFlow 는 Authorization Code + PKCE 로그인 시도 하나다. verifier 와 state 는 생성 시 고정된다 — AuthURL 로 사용자를 보내고, 콜백으로 받은 code·state 를 Exchange 에 넘긴다.

func NewAuthCodeFlow

func NewAuthCodeFlow(cfg OIDCConfig, ep OIDCEndpoints, redirectURI string) (*AuthCodeFlow, error)

NewAuthCodeFlow 는 로그인 시도를 만든다. redirectURI 는 콜백 수신 주소 (예: http://127.0.0.1:<port>/callback) — 리슨은 트랙 A 몫이다.

func (*AuthCodeFlow) AuthURL

func (f *AuthCodeFlow) AuthURL() string

AuthURL 은 사용자를 보낼 인가 URL 이다 (PKCE S256 challenge + state 포함).

func (*AuthCodeFlow) Exchange

func (f *AuthCodeFlow) Exchange(ctx context.Context, hc *http.Client, code, state string) (Session, error)

Exchange 는 콜백으로 받은 code 를 토큰으로 바꾼다. state 가 이 시도의 것과 다르면 token endpoint 에 가지 않고 거부한다.

func (*AuthCodeFlow) State

func (f *AuthCodeFlow) State() string

State 는 콜백 검증용 state 값이다.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client 는 /api/v1/* REST 의 얇은 클라이언트다. 비즈니스 로직도, 재시도도 없다 — 서버가 판정하고, 재시도 여부는 호출한 자동화(스크립트·에이전트)가 정한다 (컨셉 문서 §5, Automation 계약).

func New

func New(cfg Config, opts ...Option) (*Client, error)

New 는 해석이 끝난 Config 로 클라이언트를 만든다. 서버 주소는 필수다. 토큰은 선택이다 — dev 모드(auth.mode=session)는 토큰 없이 동작한다.

func (*Client) CheckVersionSkew

func (c *Client) CheckVersionSkew(ctx context.Context) (VersionSkew, error)

CheckVersionSkew 는 서버 버전을 조회해 최소 호환 버전과 비교한다. 서버 도달 실패는 *APIError, 버전 파싱 불능은 일반 오류로 돌아온다.

func (*Client) Do

func (c *Client) Do(ctx context.Context, method, path string, in, out any) error

Do 는 API 를 한 번 호출한다. path 는 "/api/v1/..." 절대 경로. in 이 nil 이 아니면 JSON body 로 보내고, out 이 nil 이 아니면 응답 JSON 을 디코딩한다. 2xx 가 아니면 *APIError 를 반환한다.

func (*Client) ServerInfo

func (c *Client) ServerInfo(ctx context.Context) (ServerInfo, error)

ServerInfo 는 서버 상태·버전을 조회한다.

type Config

type Config struct {
	Server string // API 서버 base URL (예: https://nullus.example.com)
	Token  string
}

Config 는 해석이 끝난 클라이언트 설정이다.

func Load

func Load(explicit Config) (Config, error)

Load 는 우선순위 명시 값(플래그) > NULLUS_* env > ~/.nullus/ 파일로 설정을 모은다. 값이 하나도 없어도 오류가 아니다 — 서버 필수 검증은 New 가 한다 (dev 모드는 토큰 없이도 동작해야 하므로 토큰은 어디서도 강제하지 않는다).

type Kind

type Kind int

Kind 는 API 호출 실패의 분류다. 값은 Automation 계약 §1 의 exit code 와 일치한다 — CLI 는 Kind.ExitCode() 를 그대로 프로세스 종료 코드로 쓰고, MCP 는 isError 메시지의 카테고리로 쓴다.

const (
	KindUsage    Kind = 2 // 잘못된 요청 — 400, 409 등 4xx (아래 예외 제외)
	KindAuth     Kind = 3 // 인증·권한 — 401, 403
	KindNotFound Kind = 4 // 대상 없음 — 404
	KindServer   Kind = 5 // 서버 오류·연결 실패 — 5xx, transport 오류
)

func (Kind) ExitCode

func (k Kind) ExitCode() int

ExitCode 는 automation 계약의 프로세스 종료 코드를 반환한다.

func (Kind) String

func (k Kind) String() string

type OIDCConfig

type OIDCConfig struct {
	Issuer   string   // 예: https://keycloak.example.com/realms/nullus
	ClientID string   // public client — PKCE 전제, client secret 없음
	Scopes   []string // 비면 ["openid"]
}

OIDCConfig 는 로그인에 필요한 IdP 좌표다. 서버는 이를 노출하는 API 가 없으므로(auth 모듈은 POST /auth/login 뿐) 트랙 A 가 플래그/env/설정 파일에서 모아 전달한다.

type OIDCEndpoints

type OIDCEndpoints struct {
	Authorization string `json:"authorization_endpoint"`
	Token         string `json:"token_endpoint"`
}

OIDCEndpoints 는 discovery 결과 중 이 라이브러리가 쓰는 것만 담는다.

func DiscoverOIDC

func DiscoverOIDC(ctx context.Context, hc *http.Client, issuer string) (OIDCEndpoints, error)

DiscoverOIDC 는 issuer 의 /.well-known/openid-configuration 을 읽는다. hc 가 nil 이면 http.DefaultClient 를 쓴다.

type Option

type Option func(*Client)

Option 은 Client 생성 옵션이다.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient 는 기본 http.Client 를 교체한다 (테스트·타임아웃 조정용).

type ServerInfo

type ServerInfo struct {
	Status  string `json:"status"`
	DB      string `json:"db"`
	Version string `json:"version"`
}

ServerInfo 는 GET /health 응답이다. /health 는 인증 없이 열려 있어 로그인 전 버전 검사에도 쓸 수 있다.

type Session

type Session struct {
	AccessToken   string    `json:"access_token"`
	RefreshToken  string    `json:"refresh_token,omitempty"`
	Expiry        time.Time `json:"expiry"` // access token 만료 시각
	Issuer        string    `json:"issuer"`
	ClientID      string    `json:"client_id"`
	TokenEndpoint string    `json:"token_endpoint"`
}

Session 은 nullus login(OIDC)이 남기는 로그인 상태다. access token 외에 refresh 에 필요한 재료(refresh token, token endpoint, client ID)까지 담아 다음 프로세스가 discovery 없이 갱신을 이어받는다.

bootstrap 토큰(무인 경로)은 세션을 만들지 않는다 — 그쪽은 S-2 의 토큰 파일만 쓰고 만료 관리는 외부(발급 스크립트) 책임이다.

func ReadSession

func ReadSession() (Session, bool, error)

ReadSession 은 저장된 세션을 읽는다. 부재는 (zero, false, nil) — 로그인 안내의 재료이지 오류가 아니다.

func RefreshSession

func RefreshSession(ctx context.Context, hc *http.Client, s Session) (Session, error)

RefreshSession 은 refresh token 으로 access token 을 갱신한 세션을 돌려준다. refresh token 이 없거나 IdP 가 invalid_grant 로 거부하면 ErrLoginRequired.

type VersionSkew

type VersionSkew struct {
	ServerVersion string // 서버가 보고한 원문 — 경고 문구용
	MinSupported  string // = MinServerVersion
	Compatible    bool   // 서버 버전 >= 최소 호환 버전
}

VersionSkew 는 클라이언트-서버 버전 스큐 판정 결과다. 경고를 낼지(stderr) 중단할지(Automation 계약 §1, exit 5)는 호출측이 정한다.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL