Skip to content

Commit 1e72979

Browse files
committed
Platform hardening: 99/99 Python tests + 19/19 Go challenges passing
Infrastructure: - Regenerate all TLS certificates with ECDSA P-256 (10-year expiry) - Fix certificate generation script to use prime256v1 curves - Add Kubernetes manifests: network policies, ArgoCD, ExternalDNS, PVCs - Add Terraform outputs for AWS/Azure/GCP configs - Add Helm templates Services: - API Gateway: security headers middleware, audit logging, Redis rate limiting - API Gateway: fix nil pointer panic in convertToOpenAIFormat - API Gateway: optimize audit logging with persistent file handle - Auth Service: HSM service with AES-GCM encryption - Monitoring: incident detection, metrics endpoints, alerts, compliance reporting - Inference Pool: add claude-3-sonnet and deepseek-chat models Scripts: - Fix start_all_services.sh PORT env var passing for each service - Fix REDIS_HOST to include port (localhost:6379) Tests: - Fix all Python test failures (production URLs, dict iteration, auth tokens) - Remove all pytest skips by implementing missing features - Update conftest.py with real auth token fixture Database: - Clean test users, retain admin/demo/testuser/integrationtest Docs: - Update user manual and execution plan with correct ports
1 parent 8c14492 commit 1e72979

124 files changed

Lines changed: 4878 additions & 2318 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci-cd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
branches: [ main ]
88

99
env:
10-
GO_VERSION: 1.21
10+
GO_VERSION: 1.24.0
1111
NODE_VERSION: 18
1212

1313
jobs:

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[submodule "Containers"]
2+
path = Containers
3+
url = git@github.com:vasic-digital/Containers.git
4+
[submodule "Challenges"]
5+
path = Challenges
6+
url = git@github.com:vasic-digital/Challenges.git

Challenges

Submodule Challenges added at 824f8dc

Containers

Submodule Containers added at db0d9f9

HELIXFLOW_COMPLETE_USER_MANUAL.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ jwt:
216216
```yaml
217217
# config/inference-pool.yaml
218218
server:
219-
grpc_port: 50052
219+
grpc_port: 50051
220220
tls:
221221
enabled: true
222222
cert_file: "/certs/inference-pool.crt"
@@ -252,7 +252,7 @@ monitoring:
252252
# config/monitoring.yaml
253253
server:
254254
http_port: 8083
255-
grpc_port: 50053
255+
grpc_port: 50055
256256
tls:
257257
enabled: true
258258
cert_file: "/certs/monitoring.crt"
@@ -1007,7 +1007,7 @@ docker logs -f helixflow-auth-service
10071007
API Gateway HTTP: 8443
10081008
API Gateway gRPC: 9443
10091009
Auth Service gRPC: 50051
1010-
Inference Pool gRPC: 50052
1010+
Inference Pool gRPC: 50051
10111011
Monitoring HTTP: 8083
10121012
Prometheus: 9090
10131013
Grafana: 3000

PHASE1_IMPLEMENTATION_STATUS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ curl http://localhost:8443/health
231231
🟢 API Gateway: http://localhost:8443 (TLS 1.3, Real AI)
232232
🟢 gRPC Gateway: localhost:9443 (gRPC with mTLS)
233233
🟢 Auth Service: gRPC:50051 (JWT + Real Authentication)
234-
🟢 Inference Pool: gRPC:50052 (Real AI Models)
234+
🟢 Inference Pool: gRPC:50051 (Real AI Models)
235235
🟢 Monitoring: http://localhost:8083 (Full gRPC + HTTP)
236236
```
237237

api-gateway/src/go.mod

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
module helixflow/api-gateway
22

3-
go 1.22.2
3+
go 1.24.0
44

55
require (
66
github.com/gorilla/mux v1.8.1
77
github.com/gorilla/websocket v1.5.3
88
github.com/redis/go-redis/v9 v9.17.2
9-
google.golang.org/grpc v1.64.0
10-
google.golang.org/protobuf v1.33.0
9+
google.golang.org/grpc v1.80.0
10+
google.golang.org/protobuf v1.36.11
1111
helixflow/auth v0.0.0-00010101000000-000000000000
1212
helixflow/inference v0.0.0-00010101000000-000000000000
1313
helixflow/monitoring v0.0.0-00010101000000-000000000000
@@ -16,10 +16,10 @@ require (
1616
require (
1717
github.com/cespare/xxhash/v2 v2.3.0 // indirect
1818
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
19-
golang.org/x/net v0.22.0 // indirect
20-
golang.org/x/sys v0.18.0 // indirect
21-
golang.org/x/text v0.17.0 // indirect
22-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
19+
golang.org/x/net v0.49.0 // indirect
20+
golang.org/x/sys v0.40.0 // indirect
21+
golang.org/x/text v0.33.0 // indirect
22+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
2323
)
2424

2525
replace helixflow/auth => ../../helixflow/auth

api-gateway/src/go.sum

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,45 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
66
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
77
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
88
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
9-
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
10-
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
9+
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
10+
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
11+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
12+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
13+
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
14+
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
15+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
16+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
17+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
18+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
1119
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
1220
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
1321
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
1422
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
1523
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
1624
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
17-
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
18-
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
19-
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
20-
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
21-
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
22-
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
23-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
24-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
25-
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
26-
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
27-
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
28-
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
25+
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
26+
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
27+
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
28+
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
29+
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
30+
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
31+
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
32+
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
33+
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
34+
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
35+
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
36+
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
37+
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
38+
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
39+
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
40+
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
41+
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
42+
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
43+
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
44+
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
45+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
46+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
47+
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
48+
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
49+
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
50+
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=

api-gateway/src/inference_handler.go

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
"context"
5-
"crypto/tls"
65
"encoding/json"
76
"fmt"
87
"net/http"
@@ -11,6 +10,7 @@ import (
1110

1211
"google.golang.org/grpc"
1312
"google.golang.org/grpc/credentials"
13+
"google.golang.org/grpc/credentials/insecure"
1414
inference "helixflow/inference"
1515
)
1616

@@ -22,20 +22,20 @@ type InferenceHandler struct {
2222

2323
// NewInferenceHandler creates a new inference handler
2424
func NewInferenceHandler(inferencePoolURL string) (*InferenceHandler, error) {
25-
var creds credentials.TransportCredentials
25+
var dialOpts []grpc.DialOption
2626
if strings.Contains(inferencePoolURL, "localhost") || strings.Contains(inferencePoolURL, "127.0.0.1") {
27-
creds = credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})
27+
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
2828
} else {
2929
// Try to load TLS certificates for secure connections
3030
certPath := "./certs/inference-pool.crt"
3131
loadedCreds, err := credentials.NewClientTLSFromFile(certPath, "inference-pool")
3232
if err != nil {
3333
return nil, fmt.Errorf("failed to load certificates: %w", err)
3434
}
35-
creds = loadedCreds
35+
dialOpts = append(dialOpts, grpc.WithTransportCredentials(loadedCreds))
3636
}
3737

38-
conn, err := grpc.Dial(inferencePoolURL, grpc.WithTransportCredentials(creds))
38+
conn, err := grpc.Dial(inferencePoolURL, dialOpts...)
3939
if err != nil {
4040
return nil, fmt.Errorf("failed to connect to inference pool: %w", err)
4141
}
@@ -70,6 +70,9 @@ func (h *InferenceHandler) HandleChatCompletion(ctx context.Context, req ChatCom
7070
if err != nil {
7171
return nil, fmt.Errorf("inference failed: %w", err)
7272
}
73+
if response == nil {
74+
return nil, fmt.Errorf("inference returned nil response")
75+
}
7376

7477
// Convert response to OpenAI format
7578
return h.convertToOpenAIFormat(response, req), nil
@@ -195,6 +198,43 @@ func convertMessages(messages []ChatMessage) []*inference.ChatMessage {
195198
}
196199
// convertToOpenAIFormat converts inference response to OpenAI format
197200
func (h *InferenceHandler) convertToOpenAIFormat(resp *inference.InferenceResponse, req ChatCompletionRequest) *ChatCompletionResponse {
201+
// Handle empty choices gracefully
202+
if len(resp.Choices) == 0 {
203+
promptTokens := 0
204+
completionTokens := 0
205+
totalTokens := 0
206+
if resp.Usage != nil {
207+
promptTokens = int(resp.Usage.PromptTokens)
208+
completionTokens = int(resp.Usage.CompletionTokens)
209+
totalTokens = int(resp.Usage.TotalTokens)
210+
}
211+
return &ChatCompletionResponse{
212+
ID: fmt.Sprintf("chatcmpl-%d", time.Now().UnixNano()),
213+
Object: "chat.completion",
214+
Created: time.Now().Unix(),
215+
Model: req.Model,
216+
Choices: []ChatCompletionChoice{},
217+
Usage: Usage{
218+
PromptTokens: promptTokens,
219+
CompletionTokens: completionTokens,
220+
TotalTokens: totalTokens,
221+
},
222+
}
223+
}
224+
promptTokens := 0
225+
completionTokens := 0
226+
totalTokens := 0
227+
if resp.Usage != nil {
228+
promptTokens = int(resp.Usage.PromptTokens)
229+
completionTokens = int(resp.Usage.CompletionTokens)
230+
totalTokens = int(resp.Usage.TotalTokens)
231+
}
232+
content := ""
233+
finishReason := ""
234+
if len(resp.Choices) > 0 && resp.Choices[0].Message != nil {
235+
content = resp.Choices[0].Message.Content
236+
finishReason = resp.Choices[0].FinishReason
237+
}
198238
return &ChatCompletionResponse{
199239
ID: fmt.Sprintf("chatcmpl-%d", time.Now().UnixNano()),
200240
Object: "chat.completion",
@@ -203,14 +243,14 @@ func (h *InferenceHandler) convertToOpenAIFormat(resp *inference.InferenceRespon
203243
Choices: []ChatCompletionChoice{
204244
{
205245
Index: 0,
206-
Message: ChatMessage{Role: "assistant", Content: resp.Choices[0].Message.Content},
207-
FinishReason: resp.Choices[0].FinishReason,
246+
Message: ChatMessage{Role: "assistant", Content: content},
247+
FinishReason: finishReason,
208248
},
209249
},
210250
Usage: Usage{
211-
PromptTokens: int(resp.Usage.PromptTokens),
212-
CompletionTokens: int(resp.Usage.CompletionTokens),
213-
TotalTokens: int(resp.Usage.TotalTokens),
251+
PromptTokens: promptTokens,
252+
CompletionTokens: completionTokens,
253+
TotalTokens: totalTokens,
214254
},
215255
}
216256
}

0 commit comments

Comments
 (0)