 131868bdca
			
		
	
	131868bdca
	
	
	
		
			
			Major security, observability, and configuration improvements:
## Security Hardening
- Implemented configurable CORS (no more wildcards)
- Added comprehensive auth middleware for admin endpoints
- Enhanced webhook HMAC validation
- Added input validation and rate limiting
- Security headers and CSP policies
## Configuration Management
- Made N8N webhook URL configurable (WHOOSH_N8N_BASE_URL)
- Replaced all hardcoded endpoints with environment variables
- Added feature flags for LLM vs heuristic composition
- Gitea fetch hardening with EAGER_FILTER and FULL_RESCAN options
## API Completeness
- Implemented GetCouncilComposition function
- Added GET /api/v1/councils/{id} endpoint
- Council artifacts API (POST/GET /api/v1/councils/{id}/artifacts)
- /admin/health/details endpoint with component status
- Database lookup for repository URLs (no hardcoded fallbacks)
## Observability & Performance
- Added OpenTelemetry distributed tracing with goal/pulse correlation
- Performance optimization database indexes
- Comprehensive health monitoring
- Enhanced logging and error handling
## Infrastructure
- Production-ready P2P discovery (replaces mock implementation)
- Removed unused Redis configuration
- Enhanced Docker Swarm integration
- Added migration files for performance indexes
## Code Quality
- Comprehensive input validation
- Graceful error handling and failsafe fallbacks
- Backwards compatibility maintained
- Following security best practices
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
		
	
		
			
				
	
	
		
			183 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2012 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| /*
 | |
| Package box authenticates and encrypts small messages using public-key cryptography.
 | |
| 
 | |
| Box uses Curve25519, XSalsa20 and Poly1305 to encrypt and authenticate
 | |
| messages. The length of messages is not hidden.
 | |
| 
 | |
| It is the caller's responsibility to ensure the uniqueness of nonces—for
 | |
| example, by using nonce 1 for the first message, nonce 2 for the second
 | |
| message, etc. Nonces are long enough that randomly generated nonces have
 | |
| negligible risk of collision.
 | |
| 
 | |
| Messages should be small because:
 | |
| 
 | |
| 1. The whole message needs to be held in memory to be processed.
 | |
| 
 | |
| 2. Using large messages pressures implementations on small machines to decrypt
 | |
| and process plaintext before authenticating it. This is very dangerous, and
 | |
| this API does not allow it, but a protocol that uses excessive message sizes
 | |
| might present some implementations with no other choice.
 | |
| 
 | |
| 3. Fixed overheads will be sufficiently amortised by messages as small as 8KB.
 | |
| 
 | |
| 4. Performance may be improved by working with messages that fit into data caches.
 | |
| 
 | |
| Thus large amounts of data should be chunked so that each message is small.
 | |
| (Each message still needs a unique nonce.) If in doubt, 16KB is a reasonable
 | |
| chunk size.
 | |
| 
 | |
| This package is interoperable with NaCl: https://nacl.cr.yp.to/box.html.
 | |
| Anonymous sealing/opening is an extension of NaCl defined by and interoperable
 | |
| with libsodium:
 | |
| https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes.
 | |
| */
 | |
| package box // import "golang.org/x/crypto/nacl/box"
 | |
| 
 | |
| import (
 | |
| 	cryptorand "crypto/rand"
 | |
| 	"io"
 | |
| 
 | |
| 	"golang.org/x/crypto/blake2b"
 | |
| 	"golang.org/x/crypto/curve25519"
 | |
| 	"golang.org/x/crypto/nacl/secretbox"
 | |
| 	"golang.org/x/crypto/salsa20/salsa"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	// Overhead is the number of bytes of overhead when boxing a message.
 | |
| 	Overhead = secretbox.Overhead
 | |
| 
 | |
| 	// AnonymousOverhead is the number of bytes of overhead when using anonymous
 | |
| 	// sealed boxes.
 | |
| 	AnonymousOverhead = Overhead + 32
 | |
| )
 | |
| 
 | |
| // GenerateKey generates a new public/private key pair suitable for use with
 | |
| // Seal and Open.
 | |
| func GenerateKey(rand io.Reader) (publicKey, privateKey *[32]byte, err error) {
 | |
| 	publicKey = new([32]byte)
 | |
| 	privateKey = new([32]byte)
 | |
| 	_, err = io.ReadFull(rand, privateKey[:])
 | |
| 	if err != nil {
 | |
| 		publicKey = nil
 | |
| 		privateKey = nil
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	curve25519.ScalarBaseMult(publicKey, privateKey)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| var zeros [16]byte
 | |
| 
 | |
| // Precompute calculates the shared key between peersPublicKey and privateKey
 | |
| // and writes it to sharedKey. The shared key can be used with
 | |
| // OpenAfterPrecomputation and SealAfterPrecomputation to speed up processing
 | |
| // when using the same pair of keys repeatedly.
 | |
| func Precompute(sharedKey, peersPublicKey, privateKey *[32]byte) {
 | |
| 	curve25519.ScalarMult(sharedKey, privateKey, peersPublicKey)
 | |
| 	salsa.HSalsa20(sharedKey, &zeros, sharedKey, &salsa.Sigma)
 | |
| }
 | |
| 
 | |
| // Seal appends an encrypted and authenticated copy of message to out, which
 | |
| // will be Overhead bytes longer than the original and must not overlap it. The
 | |
| // nonce must be unique for each distinct message for a given pair of keys.
 | |
| func Seal(out, message []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) []byte {
 | |
| 	var sharedKey [32]byte
 | |
| 	Precompute(&sharedKey, peersPublicKey, privateKey)
 | |
| 	return secretbox.Seal(out, message, nonce, &sharedKey)
 | |
| }
 | |
| 
 | |
| // SealAfterPrecomputation performs the same actions as Seal, but takes a
 | |
| // shared key as generated by Precompute.
 | |
| func SealAfterPrecomputation(out, message []byte, nonce *[24]byte, sharedKey *[32]byte) []byte {
 | |
| 	return secretbox.Seal(out, message, nonce, sharedKey)
 | |
| }
 | |
| 
 | |
| // Open authenticates and decrypts a box produced by Seal and appends the
 | |
| // message to out, which must not overlap box. The output will be Overhead
 | |
| // bytes smaller than box.
 | |
| func Open(out, box []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) ([]byte, bool) {
 | |
| 	var sharedKey [32]byte
 | |
| 	Precompute(&sharedKey, peersPublicKey, privateKey)
 | |
| 	return secretbox.Open(out, box, nonce, &sharedKey)
 | |
| }
 | |
| 
 | |
| // OpenAfterPrecomputation performs the same actions as Open, but takes a
 | |
| // shared key as generated by Precompute.
 | |
| func OpenAfterPrecomputation(out, box []byte, nonce *[24]byte, sharedKey *[32]byte) ([]byte, bool) {
 | |
| 	return secretbox.Open(out, box, nonce, sharedKey)
 | |
| }
 | |
| 
 | |
| // SealAnonymous appends an encrypted and authenticated copy of message to out,
 | |
| // which will be AnonymousOverhead bytes longer than the original and must not
 | |
| // overlap it. This differs from Seal in that the sender is not required to
 | |
| // provide a private key.
 | |
| func SealAnonymous(out, message []byte, recipient *[32]byte, rand io.Reader) ([]byte, error) {
 | |
| 	if rand == nil {
 | |
| 		rand = cryptorand.Reader
 | |
| 	}
 | |
| 	ephemeralPub, ephemeralPriv, err := GenerateKey(rand)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	var nonce [24]byte
 | |
| 	if err := sealNonce(ephemeralPub, recipient, &nonce); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if total := len(out) + AnonymousOverhead + len(message); cap(out) < total {
 | |
| 		original := out
 | |
| 		out = make([]byte, 0, total)
 | |
| 		out = append(out, original...)
 | |
| 	}
 | |
| 	out = append(out, ephemeralPub[:]...)
 | |
| 
 | |
| 	return Seal(out, message, &nonce, recipient, ephemeralPriv), nil
 | |
| }
 | |
| 
 | |
| // OpenAnonymous authenticates and decrypts a box produced by SealAnonymous and
 | |
| // appends the message to out, which must not overlap box. The output will be
 | |
| // AnonymousOverhead bytes smaller than box.
 | |
| func OpenAnonymous(out, box []byte, publicKey, privateKey *[32]byte) (message []byte, ok bool) {
 | |
| 	if len(box) < AnonymousOverhead {
 | |
| 		return nil, false
 | |
| 	}
 | |
| 
 | |
| 	var ephemeralPub [32]byte
 | |
| 	copy(ephemeralPub[:], box[:32])
 | |
| 
 | |
| 	var nonce [24]byte
 | |
| 	if err := sealNonce(&ephemeralPub, publicKey, &nonce); err != nil {
 | |
| 		return nil, false
 | |
| 	}
 | |
| 
 | |
| 	return Open(out, box[32:], &nonce, &ephemeralPub, privateKey)
 | |
| }
 | |
| 
 | |
| // sealNonce generates a 24 byte nonce that is a blake2b digest of the
 | |
| // ephemeral public key and the receiver's public key.
 | |
| func sealNonce(ephemeralPub, peersPublicKey *[32]byte, nonce *[24]byte) error {
 | |
| 	h, err := blake2b.New(24, nil)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if _, err = h.Write(ephemeralPub[:]); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if _, err = h.Write(peersPublicKey[:]); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	h.Sum(nonce[:0])
 | |
| 
 | |
| 	return nil
 | |
| }
 |