 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>
		
	
		
			
				
	
	
		
			165 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright The OpenTelemetry Authors
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //     http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package trace // import "go.opentelemetry.io/otel/sdk/trace"
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"time"
 | |
| 
 | |
| 	"go.opentelemetry.io/otel/sdk/instrumentation"
 | |
| 	"go.opentelemetry.io/otel/trace"
 | |
| 	"go.opentelemetry.io/otel/trace/embedded"
 | |
| )
 | |
| 
 | |
| type tracer struct {
 | |
| 	embedded.Tracer
 | |
| 
 | |
| 	provider             *TracerProvider
 | |
| 	instrumentationScope instrumentation.Scope
 | |
| }
 | |
| 
 | |
| var _ trace.Tracer = &tracer{}
 | |
| 
 | |
| // Start starts a Span and returns it along with a context containing it.
 | |
| //
 | |
| // The Span is created with the provided name and as a child of any existing
 | |
| // span context found in the passed context. The created Span will be
 | |
| // configured appropriately by any SpanOption passed.
 | |
| func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) {
 | |
| 	config := trace.NewSpanStartConfig(options...)
 | |
| 
 | |
| 	if ctx == nil {
 | |
| 		// Prevent trace.ContextWithSpan from panicking.
 | |
| 		ctx = context.Background()
 | |
| 	}
 | |
| 
 | |
| 	// For local spans created by this SDK, track child span count.
 | |
| 	if p := trace.SpanFromContext(ctx); p != nil {
 | |
| 		if sdkSpan, ok := p.(*recordingSpan); ok {
 | |
| 			sdkSpan.addChild()
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	s := tr.newSpan(ctx, name, &config)
 | |
| 	if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() {
 | |
| 		sps := tr.provider.getSpanProcessors()
 | |
| 		for _, sp := range sps {
 | |
| 			sp.sp.OnStart(ctx, rw)
 | |
| 		}
 | |
| 	}
 | |
| 	if rtt, ok := s.(runtimeTracer); ok {
 | |
| 		ctx = rtt.runtimeTrace(ctx)
 | |
| 	}
 | |
| 
 | |
| 	return trace.ContextWithSpan(ctx, s), s
 | |
| }
 | |
| 
 | |
| type runtimeTracer interface {
 | |
| 	// runtimeTrace starts a "runtime/trace".Task for the span and
 | |
| 	// returns a context containing the task.
 | |
| 	runtimeTrace(ctx context.Context) context.Context
 | |
| }
 | |
| 
 | |
| // newSpan returns a new configured span.
 | |
| func (tr *tracer) newSpan(ctx context.Context, name string, config *trace.SpanConfig) trace.Span {
 | |
| 	// If told explicitly to make this a new root use a zero value SpanContext
 | |
| 	// as a parent which contains an invalid trace ID and is not remote.
 | |
| 	var psc trace.SpanContext
 | |
| 	if config.NewRoot() {
 | |
| 		ctx = trace.ContextWithSpanContext(ctx, psc)
 | |
| 	} else {
 | |
| 		psc = trace.SpanContextFromContext(ctx)
 | |
| 	}
 | |
| 
 | |
| 	// If there is a valid parent trace ID, use it to ensure the continuity of
 | |
| 	// the trace. Always generate a new span ID so other components can rely
 | |
| 	// on a unique span ID, even if the Span is non-recording.
 | |
| 	var tid trace.TraceID
 | |
| 	var sid trace.SpanID
 | |
| 	if !psc.TraceID().IsValid() {
 | |
| 		tid, sid = tr.provider.idGenerator.NewIDs(ctx)
 | |
| 	} else {
 | |
| 		tid = psc.TraceID()
 | |
| 		sid = tr.provider.idGenerator.NewSpanID(ctx, tid)
 | |
| 	}
 | |
| 
 | |
| 	samplingResult := tr.provider.sampler.ShouldSample(SamplingParameters{
 | |
| 		ParentContext: ctx,
 | |
| 		TraceID:       tid,
 | |
| 		Name:          name,
 | |
| 		Kind:          config.SpanKind(),
 | |
| 		Attributes:    config.Attributes(),
 | |
| 		Links:         config.Links(),
 | |
| 	})
 | |
| 
 | |
| 	scc := trace.SpanContextConfig{
 | |
| 		TraceID:    tid,
 | |
| 		SpanID:     sid,
 | |
| 		TraceState: samplingResult.Tracestate,
 | |
| 	}
 | |
| 	if isSampled(samplingResult) {
 | |
| 		scc.TraceFlags = psc.TraceFlags() | trace.FlagsSampled
 | |
| 	} else {
 | |
| 		scc.TraceFlags = psc.TraceFlags() &^ trace.FlagsSampled
 | |
| 	}
 | |
| 	sc := trace.NewSpanContext(scc)
 | |
| 
 | |
| 	if !isRecording(samplingResult) {
 | |
| 		return tr.newNonRecordingSpan(sc)
 | |
| 	}
 | |
| 	return tr.newRecordingSpan(psc, sc, name, samplingResult, config)
 | |
| }
 | |
| 
 | |
| // newRecordingSpan returns a new configured recordingSpan.
 | |
| func (tr *tracer) newRecordingSpan(psc, sc trace.SpanContext, name string, sr SamplingResult, config *trace.SpanConfig) *recordingSpan {
 | |
| 	startTime := config.Timestamp()
 | |
| 	if startTime.IsZero() {
 | |
| 		startTime = time.Now()
 | |
| 	}
 | |
| 
 | |
| 	s := &recordingSpan{
 | |
| 		// Do not pre-allocate the attributes slice here! Doing so will
 | |
| 		// allocate memory that is likely never going to be used, or if used,
 | |
| 		// will be over-sized. The default Go compiler has been tested to
 | |
| 		// dynamically allocate needed space very well. Benchmarking has shown
 | |
| 		// it to be more performant than what we can predetermine here,
 | |
| 		// especially for the common use case of few to no added
 | |
| 		// attributes.
 | |
| 
 | |
| 		parent:      psc,
 | |
| 		spanContext: sc,
 | |
| 		spanKind:    trace.ValidateSpanKind(config.SpanKind()),
 | |
| 		name:        name,
 | |
| 		startTime:   startTime,
 | |
| 		events:      newEvictedQueue(tr.provider.spanLimits.EventCountLimit),
 | |
| 		links:       newEvictedQueue(tr.provider.spanLimits.LinkCountLimit),
 | |
| 		tracer:      tr,
 | |
| 	}
 | |
| 
 | |
| 	for _, l := range config.Links() {
 | |
| 		s.addLink(l)
 | |
| 	}
 | |
| 
 | |
| 	s.SetAttributes(sr.Attributes...)
 | |
| 	s.SetAttributes(config.Attributes()...)
 | |
| 
 | |
| 	return s
 | |
| }
 | |
| 
 | |
| // newNonRecordingSpan returns a new configured nonRecordingSpan.
 | |
| func (tr *tracer) newNonRecordingSpan(sc trace.SpanContext) nonRecordingSpan {
 | |
| 	return nonRecordingSpan{tracer: tr, sc: sc}
 | |
| }
 |