commit dc892f6ac42aba5361aa3ea1d95750f1515e9861
Author: Dustin Stiles <duwstiles@pm.me>
Date:   Fri Mar 7 21:05:37 2025 -0500

    initial commit

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0c066c3
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# DSFX
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..1f140e2
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module koti.casa/numenor-labs/dsfx
+
+go 1.24.1
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..8b03507
--- /dev/null
+++ b/main.go
@@ -0,0 +1,9 @@
+package main
+
+import (
+	"log"
+)
+
+func main() {
+	log.Println("Hello, World!")
+}
diff --git a/pkg/dcrypto/aead.go b/pkg/dcrypto/aead.go
new file mode 100644
index 0000000..ed799d0
--- /dev/null
+++ b/pkg/dcrypto/aead.go
@@ -0,0 +1,58 @@
+package dcrypto
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"errors"
+	"io"
+)
+
+// Encrypt uses AES-GCM to encrypt the given plaintext with the given key.
+func Encrypt(key, plaintext []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+
+	gcm, err := cipher.NewGCM(block)
+	if err != nil {
+		return nil, err
+	}
+
+	nonce := make([]byte, gcm.NonceSize())
+	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
+		return nil, err
+	}
+
+	ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
+
+	return ciphertext, nil
+}
+
+// Decrypt uses AES-GCM to decrypt the given ciphertext with the given key.
+func Decrypt(key, ciphertext []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+
+	gcm, err := cipher.NewGCM(block)
+	if err != nil {
+		return nil, err
+	}
+
+	nonceSize := gcm.NonceSize()
+	if len(ciphertext) < nonceSize {
+		return nil, errors.New("ciphertext too short")
+	}
+
+	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
+
+	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return plaintext, nil
+}
diff --git a/pkg/dcrypto/ecdh.go b/pkg/dcrypto/ecdh.go
new file mode 100644
index 0000000..4286405
--- /dev/null
+++ b/pkg/dcrypto/ecdh.go
@@ -0,0 +1,31 @@
+package dcrypto
+
+import (
+	"crypto/ecdh"
+	"crypto/rand"
+)
+
+var (
+	// DefaultDHCurve is the default elliptic curve used for signing.
+	DefaultDHCurve = ecdh.P384
+)
+
+// GenerateDHKey generates a new ECDH private key for key exchange.
+func GenerateDHKey() (*ecdh.PrivateKey, error) {
+	return DefaultDHCurve().GenerateKey(rand.Reader)
+}
+
+// ComputeDHSecret computes the shared secret from the private key and the public key.
+func ComputeDHSecret(priv *ecdh.PrivateKey, pub *ecdh.PublicKey) ([]byte, error) {
+	return priv.ECDH(pub)
+}
+
+// ExportDHPublicKey exports the public key as a byte slice.
+func ExportDHPublicKey(pub *ecdh.PublicKey) ([]byte, error) {
+	return pub.Bytes(), nil
+}
+
+// ImportDHPublicKey imports the public key from a byte slice.
+func ImportDHPublicKey(data []byte) (*ecdh.PublicKey, error) {
+	return DefaultDHCurve().NewPublicKey(data)
+}
diff --git a/pkg/dcrypto/ecdsa.go b/pkg/dcrypto/ecdsa.go
new file mode 100644
index 0000000..e1996b6
--- /dev/null
+++ b/pkg/dcrypto/ecdsa.go
@@ -0,0 +1,62 @@
+package dcrypto
+
+import (
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"encoding/json"
+	"math/big"
+)
+
+var (
+	// DefaultSigningCurve is the default elliptic curve used for signing.
+	DefaultSigningCurve = elliptic.P384
+)
+
+// GenerateSigningKey generates a new ECDSA private key for signing.
+func GenerateSigningKey() (*ecdsa.PrivateKey, error) {
+	return ecdsa.GenerateKey(DefaultSigningCurve(), rand.Reader)
+}
+
+// Sign signs the data with the private key.
+func Sign(priv *ecdsa.PrivateKey, data []byte) ([]byte, error) {
+	return ecdsa.SignASN1(rand.Reader, priv, data)
+}
+
+// Verify verifies the signature of the data with the public key.
+func Verify(pub *ecdsa.PublicKey, data, signature []byte) bool {
+	return ecdsa.VerifyASN1(pub, data, signature)
+}
+
+// ExportPublicSigningKey exports the public key as a byte slice.
+func ExportPublicSigningKey(pub *ecdsa.PublicKey) ([]byte, error) {
+	data := struct {
+		N    []byte `json:"n"`
+		PubX []byte `json:"pub_x"`
+		PubY []byte `json:"pub_y"`
+	}{
+		N:    pub.Curve.Params().N.Bytes(),
+		PubX: pub.X.Bytes(),
+		PubY: pub.Y.Bytes(),
+	}
+	return json.Marshal(data)
+}
+
+// ImportPublicSigningKey imports the public key from a byte slice.
+func ImportPublicSigningKey(pubBytes []byte) (*ecdsa.PublicKey, error) {
+	var data struct {
+		N    []byte `json:"n"`
+		PubX []byte `json:"pub_x"`
+		PubY []byte `json:"pub_y"`
+	}
+	if err := json.Unmarshal(pubBytes, &data); err != nil {
+		return nil, err
+	}
+
+	params := new(ecdsa.PublicKey)
+	params.Curve = DefaultSigningCurve()
+	params.X = new(big.Int).SetBytes(data.PubX)
+	params.Y = new(big.Int).SetBytes(data.PubY)
+
+	return params, nil
+}
diff --git a/pkg/dcrypto/hkdf.go b/pkg/dcrypto/hkdf.go
new file mode 100644
index 0000000..909cb11
--- /dev/null
+++ b/pkg/dcrypto/hkdf.go
@@ -0,0 +1,11 @@
+package dcrypto
+
+import (
+	"crypto/hkdf"
+	"crypto/sha256"
+)
+
+// Key creates a key from the given secret and salt using HKDF.
+func Key(secret, salt []byte) ([]byte, error) {
+	return hkdf.Key(sha256.New, secret, salt, "", 32)
+}
diff --git a/pkg/dcrypto/rand.go b/pkg/dcrypto/rand.go
new file mode 100644
index 0000000..b72b239
--- /dev/null
+++ b/pkg/dcrypto/rand.go
@@ -0,0 +1,12 @@
+package dcrypto
+
+import "crypto/rand"
+
+// Challenge generates a random challenge of n bytes.
+func Challenge(n int) []byte {
+	challenge := make([]byte, n)
+	if _, err := rand.Read(challenge); err != nil || len(challenge) != n {
+		return nil
+	}
+	return challenge
+}
diff --git a/pkg/encoding/vwb/vwb.go b/pkg/encoding/vwb/vwb.go
new file mode 100644
index 0000000..311baeb
--- /dev/null
+++ b/pkg/encoding/vwb/vwb.go
@@ -0,0 +1,55 @@
+// Package vwb provides utilities for working with variable-width byte slices,
+// usually in the context of network requests.
+package vwb
+
+import (
+	"fmt"
+	"io"
+)
+
+func debugf(msg string, args ...any) {
+	fmt.Printf("(encoding/vwb): "+msg, args...)
+}
+
+// Encode ...
+func Encode(w io.Writer, input []byte) error {
+	debugf("encoding %d bytes\n", len(input))
+	buf := make([]byte, 0, len(input)+2)
+
+	// Write the length of the input
+	buf = append(buf, byte(len(input)>>8), byte(len(input)))
+	// Write the input
+	buf = append(buf, input...)
+
+	debugf("writing %d bytes\n", len(buf))
+	// Write the buffer to the writer
+	if _, err := w.Write(buf); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Decode ...
+func Decode(r io.Reader) ([]byte, error) {
+	debugf("beginning decode\n")
+	var lenbuf [2]byte
+	// Read the length of the input
+	if _, err := io.ReadFull(r, lenbuf[:]); err != nil {
+		return nil, err
+	}
+
+	debugf("read %d bytes for length\n", lenbuf)
+	// Calculate the length of the input
+	length := int(lenbuf[0])<<8 | int(lenbuf[1])
+
+	debugf("reading %d byte sized message\n", length)
+	// Read the input
+	data := make([]byte, length)
+	if _, err := io.ReadFull(r, data); err != nil {
+		return nil, err
+	}
+
+	debugf("done\n")
+	return data, nil
+}
diff --git a/pkg/handshake/handshake.go b/pkg/handshake/handshake.go
new file mode 100644
index 0000000..375fffe
--- /dev/null
+++ b/pkg/handshake/handshake.go
@@ -0,0 +1,347 @@
+package handshake
+
+import (
+	"crypto/ecdh"
+	"crypto/ecdsa"
+	"crypto/sha256"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+
+	"koti.casa/numenor-labs/dsfx/pkg/dcrypto"
+	"koti.casa/numenor-labs/dsfx/pkg/encoding/vwb"
+)
+
+func debugf(msg string, args ...any) {
+	fmt.Printf("(debug): "+msg, args...)
+}
+
+// An Actor represents an entity that can participate in the handshake process.
+// In this model, there are two actors: Alice and Bob. Alice represents the
+// client role and Bob represents the server role. Each actor has a ecdsa private
+// key used for identification, and a tcp address that they are reachable at.
+type Actor interface {
+	// Identity represents the identity of the actor. It is used to sign messages
+	// on behalf of the actor during the handshake process. The actor may choose
+	// to use a well known key here in order to be recognized by other actors.
+	// It is also valid to return an ephemeral key here if the actor wishes to
+	// remain anonymous with the remote actor.
+	IdentityKey() *ecdsa.PrivateKey
+
+	// Address returns the address of the actor. This is used when listening for
+	// incoming connections, and when dialing out to other actors.
+	Address() *net.TCPAddr
+}
+
+// State represents the state of the handshake process. It should be passed to
+// and from the various functions in the handshake process.
+type State struct {
+	EphemeralKey *ecdh.PrivateKey
+}
+
+// InitiateHandshake initiates the handshake process between the given actor
+// and the remote actor.
+func InitiateHandshake(actor Actor, conn io.ReadWriter, rpub *ecdsa.PublicKey) ([]byte, error) {
+	// ------------------------------------------------------------------------
+	// Step 1: Ephemeral Key Exchange To Server
+
+	debugf("client: creating dh key\n")
+	// Create a new ECDH private key for the actor.
+	ourDHKey, err := dcrypto.GenerateDHKey()
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: exporting dh key\n")
+	// Export the public key of the actor's ECDH private key.
+	ourDHKeyRaw, err := dcrypto.ExportDHPublicKey(ourDHKey.PublicKey())
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: sending dh key\n")
+	// Write the actor's public key to the connection.
+	err = vwb.Encode(conn, ourDHKeyRaw)
+	if err != nil {
+		return nil, err
+	}
+
+	// ------------------------------------------------------------------------
+	// Step 2: Ephemeral Key Exchange From Server
+
+	debugf("client: waiting for server's dh key\n")
+	// Read the remote actor's public key from the connection.
+	remoteDHKeyRaw, err := vwb.Decode(conn)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: importing server's dh key\n")
+	// Import the remote actor's public key.
+	remoteDHKey, err := dcrypto.ImportDHPublicKey(remoteDHKeyRaw)
+	if err != nil {
+		return nil, err
+	}
+
+	// ------------------------------------------------------------------------
+	// Step 3: Client Authentication
+
+	debugf("client: exporting public signing key\n")
+	// Export the public key of the actor's signing key.
+	ourPublicKeyRaw, err := dcrypto.ExportPublicSigningKey(&actor.IdentityKey().PublicKey)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: exporting remote public signing key\n")
+	remotePublicKeyRaw, err := dcrypto.ExportPublicSigningKey(rpub)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: creating authentication message\n")
+	// Construct the message that will be signed by the client.
+	// This message is formatted as follows:
+	// rlt + sha256(ae + be)
+	// This binds both the client and server's long term public keys to the
+	// ephemeral keys that were exchanged in the previous step. This creates a
+	// verifiable link between both parties and the ephemeral keys used to
+	// establish the shared secret.
+	authMessage, err := buildMessage(ourDHKey.PublicKey(), remoteDHKey)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: signing authentication message\n")
+	// Sign the message with the actor's private key.
+	signature, err := dcrypto.Sign(actor.IdentityKey(), authMessage)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: encoding %d bytes of public key\n", len(ourPublicKeyRaw))
+	debugf("client: encoding %d bytes of signature\n", len(signature))
+
+	plaintext := make([]byte, 0, len(ourPublicKeyRaw)+len(signature))
+	plaintext = append(plaintext, ourPublicKeyRaw...)
+	plaintext = append(plaintext, signature...)
+
+	debugf("client: computing shared secret\n")
+	// Compute the shared secret between the actor and the remote actor.
+	sharedSecret, err := dcrypto.ComputeDHSecret(ourDHKey, remoteDHKey)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: deriving key from shared secret\n")
+	// Derive a key from the shared secret using HKDF.
+	derivedKey, err := dcrypto.Key(sharedSecret, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: encrypting authentication message\n")
+	// Encrypt the message with the derived key.
+	boxedMsg, err := dcrypto.Encrypt(derivedKey, plaintext)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: sending authentication message\n")
+	// Write the boxed message to the connection.
+	err = vwb.Encode(conn, boxedMsg)
+	if err != nil {
+		return nil, err
+	}
+
+	// ------------------------------------------------------------------------
+	// Step 4: Server Authentication
+
+	debugf("client: waiting for server's authentication message\n")
+	// Read the authentication message from the connection.
+	authMessageRaw, err := vwb.Decode(conn)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: decrypting authentication message\n")
+	// Decrypt the authentication message with the derived key.
+	plaintext, err = dcrypto.Decrypt(derivedKey, authMessageRaw)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: importing server's signing key\n")
+	// The server authentication is just verifying the signature it created of
+	// the client authentication message.
+	remotePublicKey, err := dcrypto.ImportPublicSigningKey(remotePublicKeyRaw)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("client: verifying server's signature\n")
+	if !dcrypto.Verify(remotePublicKey, authMessage, plaintext) {
+		return nil, errors.New("failed to verify server's signature")
+	}
+
+	debugf("client: handshake complete\n")
+	return sharedSecret, nil
+}
+
+// AcceptHandshake accepts a handshake from the given actor and connection. It
+// returns the shared secret between the actor and the remote actor.
+func AcceptHandshake(actor Actor, conn io.ReadWriter) ([]byte, error) {
+	// ------------------------------------------------------------------------
+	// Step 1: Ephemeral Key Exchange From Client
+
+	debugf("server: starting handshake process\n")
+	// Read the remote actor's public key from the connection.
+	remoteDHKeyRaw, err := vwb.Decode(conn)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("server: importing client's dh key\n")
+	// Import the remote actor's public key.
+	remoteDHKey, err := dcrypto.ImportDHPublicKey(remoteDHKeyRaw)
+	if err != nil {
+		return nil, err
+	}
+
+	// ------------------------------------------------------------------------
+	// Step 2: Ephemeral Key Exchange To Client
+
+	debugf("server: creating dh key\n")
+	// Create a new ECDH private key for the actor.
+	ourDHKey, err := dcrypto.GenerateDHKey()
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("server: exporting dh key\n")
+	// Export the public key of the actor's ECDH private key.
+	ourDHKeyRaw, err := dcrypto.ExportDHPublicKey(ourDHKey.PublicKey())
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("server: sending dh key\n")
+	// Write the actor's public key to the connection.
+	err = vwb.Encode(conn, ourDHKeyRaw)
+	if err != nil {
+		return nil, err
+	}
+
+	// ------------------------------------------------------------------------
+	// Step 3: Server Authentication
+
+	debugf("server: waiting for client's authentication message\n")
+	// Read the authentication message from the connection.
+	authMessageRaw, err := vwb.Decode(conn)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("server: computing shared secret\n")
+	// Decrypt the authentication message with the derived key.
+	sharedSecret, err := dcrypto.ComputeDHSecret(ourDHKey, remoteDHKey)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("server: deriving key from shared secret\n")
+	// Derive a key from the shared secret using HKDF.
+	derivedKey, err := dcrypto.Key(sharedSecret, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("server: decrypting authentication message\n")
+	plaintext, err := dcrypto.Decrypt(derivedKey, authMessageRaw)
+	if err != nil {
+		return nil, err
+	}
+
+	clientPublicKeyRaw := plaintext[:222]
+	signature := plaintext[222:]
+
+	debugf("server: importing client's public signing key\n")
+	// Verify the client's public key and signature.
+	clientPublicKey, err := dcrypto.ImportPublicSigningKey(clientPublicKeyRaw)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("server: verifying client's signature\n")
+	// Construct the message that was signed by the client.
+	// This message is formatted as follows:
+	// rlt + sha256(ae + be)
+	// This binds both the client and server's long term public keys to the
+	// ephemeral keys that were exchanged in the previous step. This creates a
+	// verifiable link between both parties and the ephemeral keys used to
+	// establish the shared secret.
+	authMessage, err := buildMessage(remoteDHKey, ourDHKey.PublicKey())
+	if err != nil {
+		return nil, err
+	}
+
+	if !dcrypto.Verify(clientPublicKey, authMessage, signature) {
+		return nil, errors.New("failed to verify client's signature")
+	}
+
+	debugf("server: creating authentication message\n")
+	// Now we need to sign the authentication message with the server's private
+	// key. This will be sent back to the client in the next step to authenticate
+	// the server to the client.
+	serverSignature, err := dcrypto.Sign(actor.IdentityKey(), authMessage)
+	if err != nil {
+		return nil, err
+	}
+
+	boxedMsg, err := dcrypto.Encrypt(derivedKey, serverSignature)
+	if err != nil {
+		return nil, err
+	}
+
+	debugf("server: sending authentication message\n")
+	// Send the server's signature back to the client.
+	err = vwb.Encode(conn, boxedMsg)
+	if err != nil {
+		return nil, err
+	}
+
+	// ------------------------------------------------------------------------
+	// Step 4: Client Authentication
+
+	debugf("server: handshake complete\n")
+	return sharedSecret, nil
+}
+
+func buildMessage(clientPubKey *ecdh.PublicKey, serverPubKey *ecdh.PublicKey) ([]byte, error) {
+	clientPubKeyRaw, err := dcrypto.ExportDHPublicKey(clientPubKey)
+	if err != nil {
+		return nil, err
+	}
+	serverPubKeyRaw, err := dcrypto.ExportDHPublicKey(serverPubKey)
+	if err != nil {
+		return nil, err
+	}
+	// Construct the message that will be signed by the client.
+	// This message is formatted as follows:
+	// rlt + sha256(ae + be)
+	// This binds both the client and server's long term public keys to the
+	// ephemeral keys that were exchanged in the previous step. This creates a
+	// verifiable link between both parties and the ephemeral keys used to
+	// establish the shared secret.
+	message := make([]byte, 0, len(clientPubKeyRaw)+len(serverPubKeyRaw))
+	message = append(message, clientPubKeyRaw...)
+	message = append(message, serverPubKeyRaw...)
+
+	messageChecksum := sha256.Sum256(message)
+	authMessage := make([]byte, 0, len(serverPubKeyRaw)+sha256.Size)
+	authMessage = append(authMessage, serverPubKeyRaw...)
+	authMessage = append(authMessage, messageChecksum[:]...)
+
+	return authMessage, nil
+}
diff --git a/pkg/handshake/handshake_test.go b/pkg/handshake/handshake_test.go
new file mode 100644
index 0000000..f06c082
--- /dev/null
+++ b/pkg/handshake/handshake_test.go
@@ -0,0 +1,90 @@
+package handshake_test
+
+import (
+	"bytes"
+	"crypto/ecdsa"
+	"log"
+	"net"
+	"sync"
+	"testing"
+
+	"koti.casa/numenor-labs/dsfx/pkg/dcrypto"
+	"koti.casa/numenor-labs/dsfx/pkg/handshake"
+)
+
+func TestHandshake(t *testing.T) {
+	var wg sync.WaitGroup
+
+	client, server := net.Pipe()
+	defer client.Close()
+	defer server.Close()
+
+	alice := newActor()
+	bob := newActor()
+
+	var aliceSharedSecret []byte
+	var aliceErr error
+	var bobSharedSecret []byte
+	var bobErr error
+
+	wg.Add(2)
+	go func() {
+		aliceSharedSecret, aliceErr = handshake.InitiateHandshake(alice, client, &bob.IdentityKey().PublicKey)
+		wg.Done()
+	}()
+	go func() {
+		bobSharedSecret, bobErr = handshake.AcceptHandshake(bob, server)
+		wg.Done()
+	}()
+
+	wg.Wait()
+
+	if aliceErr != nil || bobErr != nil {
+		if aliceErr != nil {
+			t.Errorf("alice error: %v", aliceErr)
+			return
+		}
+		t.Errorf("bob error: %v", bobErr)
+		return
+	}
+
+	if aliceSharedSecret == nil || bobSharedSecret == nil {
+		t.Errorf("handshake failed: shared secret is nil")
+		return
+	}
+	if len(aliceSharedSecret) == 0 || len(bobSharedSecret) == 0 {
+		t.Errorf("handshake failed: shared secret is empty")
+	}
+	if !bytes.Equal(aliceSharedSecret, bobSharedSecret) {
+		t.Errorf("handshake failed: shared secrets do not match")
+		return
+	}
+}
+
+// An actor represents an entity that can participate in the handshake process.
+type actor struct {
+	identity *ecdsa.PrivateKey
+}
+
+// newActor creates a new actor with a random identity key.
+func newActor() *actor {
+	key, err := dcrypto.GenerateSigningKey()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	return &actor{
+		identity: key,
+	}
+}
+
+// IdentityKey ...
+func (a *actor) IdentityKey() *ecdsa.PrivateKey {
+	return a.identity
+}
+
+// Address returns the address of the actor. This is used when listening for
+// incoming connections, and when dialing out to other actors.
+func (a *actor) Address() *net.TCPAddr {
+	return nil
+}
diff --git a/revive.toml b/revive.toml
new file mode 100644
index 0000000..08005fe
--- /dev/null
+++ b/revive.toml
@@ -0,0 +1,41 @@
+ignoreGeneratedHeader = false
+severity = "warning"
+confidence = 0.8
+errorCode = 1
+warningCode = 1
+
+[rule.bare-return]
+[rule.blank-imports]
+[rule.context-as-argument]
+[rule.context-keys-type]
+[rule.dot-imports]
+[rule.empty-block]
+[rule.empty-lines]
+[rule.enforce-map-style]
+[rule.enforce-slice-style]
+[rule.error-naming]
+[rule.error-return]
+[rule.error-strings]
+[rule.errorf]
+[rule.exported]
+[rule.filename-format]
+    # Override the default pattern to forbid .go files with uppercase letters and dashes.
+    arguments=["^[_a-z][_a-z0-9]*\\.go$"]
+[rule.increment-decrement]
+[rule.indent-error-flow]
+[rule.line-length-limit]
+    arguments = [200]
+# [rule.package-comments]
+[rule.range]
+[rule.receiver-naming]
+[rule.redefines-builtin-id]
+[rule.superfluous-else]
+[rule.time-naming]
+[rule.unexported-naming]
+[rule.unexported-return]
+[rule.unreachable-code]
+[rule.unused-parameter]
+[rule.useless-break]
+[rule.use-any]
+[rule.var-declaration]
+[rule.var-naming]