diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..6f7f6522ccc7094640a0b273661502680b156911
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+vendor/**/* -diff
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b8ebcf9e02d58c15298a0a00e545b7583564c9a9
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,28 @@
+stages:
+  - test
+
+before_script:
+  - ln -s /builds /go/src/code.vereign.com
+  - cd /go/src/code.vereign.com/${CI_PROJECT_PATH}
+
+unit tests:
+  image: golang:1.17.7
+  stage: test
+  tags:
+    - amd64-docker
+  script:
+    - go version
+    - go test -race -coverprofile=coverage.out ./...
+    - go tool cover -func=coverage.out
+
+lint:
+  image: golangci/golangci-lint:v1.44.2
+  stage: test
+  tags:
+    - amd64-docker
+  script:
+    - golangci-lint --version
+    - golangci-lint run
+  before_script:
+    - ln -s /builds /go/src/code.vereign.com
+    - cd /go/src/code.vereign.com/${CI_PROJECT_PATH}
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e6ba71d0fff71ba18fa18f4bd0f55fbaad679ab2
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,31 @@
+run:
+  deadline: 5m
+  skip-dirs:
+    - vendor/
+    - .*fakes/
+    - .*generated/
+  skip-files:
+    - .*generated.go
+
+linters:
+  disable-all: true
+  enable:
+    - megacheck
+    - govet
+    - deadcode
+    - errcheck
+    - goconst
+    - gocyclo
+    - goimports
+    - revive
+    - gosec
+    - ineffassign
+    - nakedret
+    - staticcheck
+    - structcheck
+    - unconvert
+    - varcheck
+    - vet
+    - vetshadow
+    - misspell
+    - staticcheck
diff --git a/errors/errors.go b/errors/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..9bd1d17dd4269461f54ec5024a6a8ca0f0edb4ac
--- /dev/null
+++ b/errors/errors.go
@@ -0,0 +1,297 @@
+// Package errors defines structured errors which can
+// be used for nesting errors with propagation
+// of error identifiers and their messages.
+// It also supports JSON serialization, so service to
+// service communication can preserve error Kind.
+package errors
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+)
+
+var separator = ": "
+
+type Kind int
+
+const (
+	Unknown      Kind = iota // Unknown error.
+	BadRequest               // BadRequest specifies invalid arguments or operation.
+	Unauthorized             // Unauthorized request.
+	Forbidden                // Forbidden operation.
+	Exist                    // Exist already.
+	NotFound                 // NotFound specifies that a resource does not exist.
+	Timeout                  // Timeout of request.
+	Internal                 // Internal error or inconsistency.
+)
+
+type Error struct {
+	// ID is a unique error identifier.
+	ID string
+
+	// Kind of error returned to the caller.
+	Kind Kind
+
+	// Message is a description of the error.
+	Message string
+
+	// The underlying error that triggered this one, if any.
+	Err error
+}
+
+func (k Kind) String() string {
+	switch k {
+	case Unknown:
+		return "unknown error"
+	case BadRequest:
+		return "bad request"
+	case Unauthorized:
+		return "not authenticated"
+	case Forbidden:
+		return "permission denied"
+	case Exist:
+		return "already exist"
+	case NotFound:
+		return "not found"
+	case Timeout:
+		return "timeout"
+	case Internal:
+		return "internal error"
+	}
+
+	return "unknown error kind"
+}
+
+// New builds an error value from its arguments.
+// There must be at least one argument or New panics.
+// The type of each argument determines its meaning.
+// If more than one argument of a given type is presented, only the last one is
+// recorded.
+//
+// The supported types are:
+//   errors.Kind:
+//       The kind of the error.
+//   *errors.Error
+//       The underlying error that triggered this one. If the error has
+//       non-empty ID and Kind fields, they are promoted as values of the
+//       returned one.
+//   error:
+//       The underlying error that triggered this one.
+//   string:
+//       Treated as an error message and assigned to the Message field.
+func New(args ...interface{}) error {
+	if len(args) == 0 {
+		panic("call to errors.New without arguments")
+	}
+
+	e := &Error{}
+	var innerKind = Unknown
+	for _, arg := range args {
+		switch arg := arg.(type) {
+		case Kind:
+			e.Kind = arg
+		case *Error:
+			copy := *arg
+			e.Err = &copy
+			e.ID = copy.ID
+			innerKind = copy.Kind
+			if e.Message == "" {
+				e.Message = copy.Message
+			}
+		case error:
+			e.Err = arg
+		case string:
+			e.Message = arg
+		}
+	}
+
+	if e.ID == "" {
+		e.ID = NewID()
+	}
+
+	if e.Kind == Unknown {
+		e.Kind = innerKind
+	}
+
+	return e
+}
+
+// Is reports whether err is an *Error of the given Kind.
+func Is(kind Kind, err error) bool {
+	cerr, ok := err.(*Error)
+	return ok && cerr.Kind == kind
+}
+
+// Error returns description of the error.
+func (e *Error) Error() string {
+	if e == nil {
+		return "nil"
+	}
+
+	if e.ID == "" {
+		e.ID = NewID()
+	}
+
+	b := new(bytes.Buffer)
+	b.WriteString(e.Message)
+
+	if e.Kind != 0 {
+		pad(b, separator)
+		b.WriteString(e.Kind.String())
+	}
+
+	if e.Err != nil {
+		pad(b, separator)
+		if cerr, ok := e.Err.(*Error); ok {
+			b.WriteString(cerr.errorSkipID())
+		} else {
+			b.WriteString(e.Err.Error())
+		}
+	}
+	b.WriteRune(' ')
+	b.WriteRune('(')
+	b.WriteString(e.ID)
+	b.WriteRune(')')
+
+	return b.String()
+}
+
+func (e *Error) errorSkipID() string {
+	if e == nil {
+		return "nil"
+	}
+	b := new(bytes.Buffer)
+	b.WriteString(e.Message)
+
+	if e.Kind != 0 {
+		pad(b, separator)
+		b.WriteString(e.Kind.String())
+	}
+	if e.Err != nil {
+		pad(b, separator)
+		if cerr, ok := e.Err.(*Error); ok {
+			b.WriteString(cerr.errorSkipID())
+		} else {
+			b.WriteString(e.Err.Error())
+		}
+	}
+	return b.String()
+}
+
+// StatusCode returns the HTTP status code corresponding to the error.
+func (e *Error) StatusCode() int {
+	switch e.Kind {
+	case BadRequest:
+		return http.StatusBadRequest
+	case Unauthorized:
+		return http.StatusUnauthorized
+	case Forbidden:
+		return http.StatusForbidden
+	case Exist:
+		return http.StatusConflict
+	case NotFound:
+		return http.StatusNotFound
+	case Timeout:
+		return http.StatusRequestTimeout
+	case Internal:
+		return http.StatusInternalServerError
+	default:
+		return http.StatusInternalServerError
+	}
+}
+
+// MarshalJSON returns the JSON representation of an Error.
+func (e *Error) MarshalJSON() (data []byte, err error) {
+	var d = struct {
+		ID      string `json:"id,omitempty"`
+		Kind    Kind   `json:"kind"`
+		Message string `json:"message,omitempty"`
+	}{
+		ID:      e.ID,
+		Kind:    e.Kind,
+		Message: e.Message,
+	}
+	return json.Marshal(d)
+}
+
+// UnmarshalJSON decodes a JSON encoded Error.
+func (e *Error) UnmarshalJSON(data []byte) error {
+	var d struct {
+		ID      string `json:"id,omitempty"`
+		Kind    Kind   `json:"kind"`
+		Message string `json:"message,omitempty"`
+	}
+	if err := json.Unmarshal(data, &d); err != nil {
+		return err
+	}
+
+	*e = Error{
+		ID:      d.ID,
+		Kind:    d.Kind,
+		Message: d.Message,
+	}
+	return nil
+}
+
+func JSON(w http.ResponseWriter, err error, statusCode ...int) {
+	var e error
+	var ok bool
+	if e, ok = err.(*Error); !ok {
+		e = New(err)
+	}
+
+	// check if the error can report its own status code
+	code := http.StatusInternalServerError
+	if sc, ok := e.(interface {
+		StatusCode() int
+	}); ok {
+		code = sc.StatusCode()
+	}
+
+	// overwrite the status code if it's explicitly passed as argument
+	if len(statusCode) > 0 {
+		code = statusCode[0]
+	}
+
+	w.WriteHeader(code)
+	w.Header().Set("Content-Type", "application/json")
+	_ = json.NewEncoder(w).Encode(e)
+}
+
+// Temporary reports if an Error is temporary and
+// whether the request can be retried.
+func (e *Error) Temporary() bool {
+	return e != nil && (e.Kind == Internal || e.Kind == Timeout)
+}
+
+// GetKind returns error kind determined
+// by the specified HTTP status code.
+func GetKind(statusCode int) Kind {
+	switch statusCode {
+	case http.StatusBadRequest:
+		return BadRequest
+	case http.StatusUnauthorized:
+		return Unauthorized
+	case http.StatusForbidden:
+		return Forbidden
+	case http.StatusConflict:
+		return Exist
+	case http.StatusNotFound:
+		return NotFound
+	case http.StatusRequestTimeout:
+		return Timeout
+	case http.StatusInternalServerError:
+		return Internal
+	default:
+		return Unknown
+	}
+}
+
+// pad appends str to the buffer if the buffer already has some data.
+func pad(b *bytes.Buffer, str string) {
+	if b.Len() == 0 {
+		return
+	}
+	b.WriteString(str)
+}
diff --git a/errors/errors_test.go b/errors/errors_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..3d1927e807003434938a0d2c6d038720d919bdcd
--- /dev/null
+++ b/errors/errors_test.go
@@ -0,0 +1,377 @@
+package errors_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"code.vereign.com/gaiax/tsa/golib/errors"
+)
+
+func TestNew(t *testing.T) {
+	e := errors.New("something went wrong")
+	assert.Implements(t, (*error)(nil), e)
+
+	// create error with from a Kind
+	e = errors.New(errors.Forbidden)
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.Forbidden, e))
+	assert.Contains(t, e.Error(), "permission denied")
+	if ec, ok := e.(*errors.Error); ok {
+		assert.NotEmpty(t, ec.ID)
+		assert.Empty(t, ec.Message)
+		assert.Equal(t, errors.Forbidden, ec.Kind)
+		assert.Nil(t, ec.Err)
+	}
+
+	// create error with a string message only
+	e = errors.New("something went wrong")
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.Unknown, e))
+	assert.Contains(t, e.Error(), "something went wrong")
+	if ec, ok := e.(*errors.Error); ok {
+		assert.NotEmpty(t, ec.ID)
+		assert.Equal(t, "something went wrong", ec.Message)
+		assert.Equal(t, errors.Unknown, ec.Kind)
+		assert.Nil(t, ec.Err)
+	}
+
+	// create error with Kind and Message
+	e = errors.New(errors.Internal, "something went wrong")
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.Internal, e))
+	assert.Contains(t, e.Error(), "something went wrong: internal error")
+	if ec, ok := e.(*errors.Error); ok {
+		assert.NotEmpty(t, ec.ID)
+		assert.Equal(t, "something went wrong", ec.Message)
+		assert.Equal(t, errors.Internal, ec.Kind)
+		assert.Nil(t, ec.Err)
+	}
+
+	// create error from a previous error
+	e = errors.New(fmt.Errorf("oops it did it again"))
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.Unknown, e))
+	assert.Contains(t, e.Error(), "oops it did it again")
+	if ec, ok := e.(*errors.Error); ok {
+		assert.NotEmpty(t, ec.ID)
+		assert.Equal(t, errors.Unknown, ec.Kind)
+		assert.NotNil(t, ec.Err)
+		assert.Equal(t, ec.Err.Error(), "oops it did it again")
+	}
+
+	// create error from a previous structured error
+	e = errors.New(errors.New(errors.Unauthorized, "no way out"))
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.Unauthorized, e))
+	assert.Contains(t, e.Error(), "no way out: not authenticated")
+	if ec, ok := e.(*errors.Error); ok {
+		assert.NotEmpty(t, ec.ID)
+		assert.Equal(t, errors.Unauthorized, ec.Kind)
+		assert.Equal(t, ec.Message, "no way out")
+		assert.NotNil(t, ec.Err)
+		assert.Contains(t, ec.Err.Error(), "no way out: not authenticated")
+	}
+
+	// create error from a previous structured error
+	e = errors.New(errors.BadRequest, "bad request", errors.New(errors.Unauthorized, "no way out"))
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.BadRequest, e))
+	assert.Contains(t, e.Error(), "bad request: no way out: not authenticated")
+	if ec, ok := e.(*errors.Error); ok {
+		assert.NotEmpty(t, ec.ID)
+		assert.Equal(t, errors.BadRequest, ec.Kind)
+		assert.Equal(t, "bad request", ec.Message)
+		assert.NotNil(t, ec.Err)
+		assert.Contains(t, ec.Err.Error(), "no way out: not authenticated")
+	}
+
+	// create error from a previous structured error and a message
+	e = errors.New("bad request", errors.New(errors.Unauthorized, "no way out"))
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.Unauthorized, e))
+	assert.Contains(t, e.Error(), "bad request: not authenticated: no way out: not authenticated")
+	if ec, ok := e.(*errors.Error); ok {
+		assert.NotEmpty(t, ec.ID)
+		assert.Equal(t, errors.Unauthorized, ec.Kind)
+		assert.Equal(t, "bad request", ec.Message)
+		assert.NotNil(t, ec.Err)
+		assert.Contains(t, ec.Err.Error(), "no way out: not authenticated")
+	}
+
+	// create error from a previous structured error and a message
+	e = errors.New(errors.BadRequest, errors.New(errors.Unauthorized, "no way out"))
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.BadRequest, e))
+	assert.Contains(t, e.Error(), "bad request: no way out: not authenticated")
+	if ec, ok := e.(*errors.Error); ok {
+		assert.NotEmpty(t, ec.ID)
+		assert.Equal(t, errors.BadRequest, ec.Kind)
+		assert.Equal(t, "no way out", ec.Message)
+		assert.NotNil(t, ec.Err)
+		assert.Contains(t, ec.Err.Error(), "no way out: not authenticated")
+	}
+
+	// create three nested errors
+	e1 := fmt.Errorf("cannot insert record")
+	e2 := errors.New("account already exists", e1)
+	e3 := errors.New("failed to create account", e2)
+	assert.Contains(t, e3.Error(), "failed to create account: account already exists: cannot insert record")
+	if ec, ok := e3.(*errors.Error); ok {
+		assert.NotEmpty(t, ec.ID)
+		assert.Equal(t, errors.Unknown, ec.Kind)
+		assert.Equal(t, "failed to create account", ec.Message)
+		assert.NotNil(t, ec.Err)
+		assert.Contains(t, ec.Err.Error(), "account already exists")
+	}
+}
+
+func TestIs(t *testing.T) {
+	e := errors.New(errors.Timeout)
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.Timeout, e))
+
+	// create error from a previous structured error and a message
+	e = errors.New(errors.Timeout, errors.New(errors.Unauthorized))
+	assert.IsType(t, &errors.Error{}, e)
+	assert.True(t, errors.Is(errors.Timeout, e))
+}
+
+func TestError_StatusCode(t *testing.T) {
+	tests := []struct {
+		name string
+		kind errors.Kind
+		code int
+	}{
+		{
+			name: "unknown error",
+			kind: errors.Unknown,
+			code: http.StatusInternalServerError,
+		},
+		{
+			name: "bad request",
+			kind: errors.BadRequest,
+			code: http.StatusBadRequest,
+		},
+		{
+			name: "unauthorized",
+			kind: errors.Unauthorized,
+			code: http.StatusUnauthorized,
+		},
+		{
+			name: "forbidden",
+			kind: errors.Forbidden,
+			code: http.StatusForbidden,
+		},
+		{
+			name: "exists",
+			kind: errors.Exist,
+			code: http.StatusConflict,
+		},
+		{
+			name: "not found",
+			kind: errors.NotFound,
+			code: http.StatusNotFound,
+		},
+		{
+			name: "timeout",
+			kind: errors.Timeout,
+			code: http.StatusRequestTimeout,
+		},
+		{
+			name: "internal",
+			kind: errors.Internal,
+			code: http.StatusInternalServerError,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			e := errors.New(test.kind)
+			assert.IsType(t, &errors.Error{}, e)
+			if ec, ok := e.(*errors.Error); ok {
+				assert.Equal(t, test.code, ec.StatusCode())
+			}
+		})
+	}
+}
+
+func TestError_MarshalJSON(t *testing.T) {
+	e := errors.New(errors.NotFound, "item does not exist")
+	ec, ok := e.(*errors.Error)
+	assert.True(t, ok)
+	id := ec.ID
+
+	data, err := ec.MarshalJSON()
+	assert.NoError(t, err)
+	assert.NotEmpty(t, data)
+
+	ee := &errors.Error{}
+	err = ee.UnmarshalJSON(data)
+	assert.NoError(t, err)
+
+	assert.Equal(t, id, ee.ID)
+	assert.Equal(t, ec.Kind, ee.Kind)
+	assert.Equal(t, ec.Message, ee.Message)
+	assert.Equal(t, ec.Error(), ee.Error())
+	assert.Equal(t, errors.NotFound, ee.Kind)
+}
+
+func TestError_Temporary(t *testing.T) {
+	e := errors.New(errors.Forbidden)
+	ec, ok := e.(*errors.Error)
+	assert.True(t, ok)
+	assert.False(t, ec.Temporary())
+
+	e = errors.New(errors.Internal)
+	ec, ok = e.(*errors.Error)
+	assert.True(t, ok)
+	assert.True(t, ec.Temporary())
+}
+
+func TestGetKind(t *testing.T) {
+	tests := []struct {
+		name string
+		code int
+		kind errors.Kind
+	}{
+		{
+			name: "undefined HTTP status code",
+			code: 9999,
+			kind: errors.Unknown,
+		},
+		{
+			name: "bad request",
+			code: http.StatusBadRequest,
+			kind: errors.BadRequest,
+		},
+		{
+			name: "unauthorized",
+			code: http.StatusUnauthorized,
+			kind: errors.Unauthorized,
+		},
+		{
+			name: "forbidden",
+			code: http.StatusForbidden,
+			kind: errors.Forbidden,
+		},
+		{
+			name: "exists",
+			code: http.StatusConflict,
+			kind: errors.Exist,
+		},
+		{
+			name: "not found",
+			code: http.StatusNotFound,
+			kind: errors.NotFound,
+		},
+		{
+			name: "timeout",
+			code: http.StatusRequestTimeout,
+			kind: errors.Timeout,
+		},
+		{
+			name: "internal",
+			code: http.StatusInternalServerError,
+			kind: errors.Internal,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			kind := errors.GetKind(test.code)
+			assert.Equal(t, test.kind, kind)
+		})
+	}
+}
+
+func TestJSON(t *testing.T) {
+	tests := []struct {
+		// input
+		name       string
+		err        error
+		statusCode int
+
+		// output
+		responseCode  int
+		responseError *errors.Error
+	}{
+		{
+			name:         "error is nil",
+			err:          nil,
+			responseCode: http.StatusInternalServerError,
+			responseError: &errors.Error{
+				Kind: errors.Unknown,
+			},
+		},
+		{
+			name:         "simple text error",
+			err:          fmt.Errorf("simple text error"),
+			responseCode: http.StatusInternalServerError,
+			responseError: &errors.Error{
+				Kind:    errors.Unknown,
+				Message: "",
+			},
+		},
+		{
+			name:         "structured error",
+			err:          errors.New("structured error"),
+			responseCode: http.StatusInternalServerError,
+			responseError: &errors.Error{
+				Kind:    errors.Unknown,
+				Message: "structured error",
+			},
+		},
+		{
+			name:         "structured error with kind",
+			err:          errors.New(errors.Forbidden, "structured error with kind"),
+			responseCode: http.StatusForbidden,
+			responseError: &errors.Error{
+				Kind:    errors.Forbidden,
+				Message: "structured error with kind",
+			},
+		},
+		{
+			name:         "structured error with kind and embedded error",
+			err:          errors.New(errors.NotFound, "structured error with kind and embedded error", fmt.Errorf("embedded error")),
+			responseCode: http.StatusNotFound,
+			responseError: &errors.Error{
+				Kind:    errors.NotFound,
+				Message: "structured error with kind and embedded error",
+				Err:     fmt.Errorf("embedded error"),
+			},
+		},
+		{
+			name:         "structured error with kind only",
+			err:          errors.New(errors.Timeout),
+			responseCode: http.StatusRequestTimeout,
+			responseError: &errors.Error{
+				Kind:    errors.Timeout,
+				Message: "",
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			rr := httptest.NewRecorder()
+			if test.statusCode > 0 {
+				errors.JSON(rr, test.err, test.statusCode)
+			} else {
+				errors.JSON(rr, test.err)
+			}
+
+			assert.Equal(t, test.responseCode, rr.Code)
+
+			var responseError *errors.Error
+			err := json.NewDecoder(rr.Body).Decode(&responseError)
+			assert.NoError(t, err)
+			assert.Equal(t, test.responseError.Kind, responseError.Kind)
+			assert.Equal(t, test.responseError.Message, responseError.Message)
+		})
+	}
+}
diff --git a/errors/shortid.go b/errors/shortid.go
new file mode 100644
index 0000000000000000000000000000000000000000..8fdf9c61a634c7f9bab630640eae576f75be924b
--- /dev/null
+++ b/errors/shortid.go
@@ -0,0 +1,24 @@
+package errors
+
+import (
+	"crypto/rand"
+	"fmt"
+)
+
+const Alphabet string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
+
+func NewID() string {
+	length := 16
+
+	ll := len(Alphabet)
+	b := make([]byte, length)
+	_, err := rand.Read(b) // generates len(b) random bytes
+	if err != nil {
+		panic(fmt.Errorf("failed to read random bytes: %v", err))
+	}
+	for i := 0; i < length; i++ {
+		b[i] = Alphabet[int(b[i])%ll]
+	}
+
+	return string(b)
+}
diff --git a/errors/shortid_test.go b/errors/shortid_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f6b3ff8edd11b02856b911808d7dd20c644bfc1
--- /dev/null
+++ b/errors/shortid_test.go
@@ -0,0 +1,20 @@
+package errors_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"code.vereign.com/gaiax/tsa/golib/errors"
+)
+
+func TestNewID(t *testing.T) {
+	for i := 0; i < 100; i++ {
+		id := errors.NewID()
+		assert.Len(t, id, 16)
+
+		for _, r := range id {
+			assert.Contains(t, errors.Alphabet, string(r))
+		}
+	}
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..3b55981cf9c4a0b9067f11ba21173209cd6b8682
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,14 @@
+module code.vereign.com/gaiax/tsa/golib
+
+go 1.17
+
+require github.com/stretchr/testify v1.7.0
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/kr/text v0.2.0 // indirect
+	github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
+	gopkg.in/yaml.v3 v3.0.0-20200601152816-913338de1bd2 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..082f086ea049718b042c4a2676260fb675474493
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,21 @@
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200601152816-913338de1bd2 h1:VEmvx0P+GVTgkNu2EdTN988YCZPcD3lo9AoczZpucwc=
+gopkg.in/yaml.v3 v3.0.0-20200601152816-913338de1bd2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=