+1
-15
cmd/plcbundle/commands/did.go
+1
-15
cmd/plcbundle/commands/did.go
···
99
99
100
100
// Resolve handle to DID with timing
101
101
ctx := context.Background()
102
-
did, handleResolveTime, err := mgr.ResolveHandleOrDID(ctx, input)
102
+
did, _, err := mgr.ResolveHandleOrDID(ctx, input)
103
103
if err != nil {
104
104
return err
105
-
}
106
-
107
-
// Show what we resolved to (if it was a handle)
108
-
if input != did && !showJSON {
109
-
fmt.Fprintf(os.Stderr, "Resolved handle '%s' → %s (in %s)\n\n",
110
-
input, did, handleResolveTime)
111
105
}
112
106
113
107
stats := mgr.GetDIDIndexStats()
···
1283
1277
}
1284
1278
}
1285
1279
1286
-
fmt.Printf("═══════════════════════════════════════════════════════════════\n")
1287
1280
fmt.Printf("✓ Lookup complete in %s\n", totalElapsed)
1288
-
if stats["exists"].(bool) {
1289
-
fmt.Printf(" Method: DID index (fast)\n")
1290
-
} else {
1291
-
fmt.Printf(" Method: Full scan (slow)\n")
1292
-
}
1293
-
fmt.Printf("═══════════════════════════════════════════════════════════════\n")
1294
-
1295
1281
return nil
1296
1282
}
1297
1283
+158
server/handlers.go
+158
server/handlers.go
···
183
183
sb.WriteString(" GET /bundle/:number Bundle metadata (JSON)\n")
184
184
sb.WriteString(" GET /data/:number Raw bundle (zstd compressed)\n")
185
185
sb.WriteString(" GET /jsonl/:number Decompressed JSONL stream\n")
186
+
sb.WriteString(" GET /op/:pointer Get single operation\n")
186
187
sb.WriteString(" GET /status Server status\n")
187
188
sb.WriteString(" GET /mempool Mempool operations (JSONL)\n")
188
189
···
232
233
sb.WriteString(fmt.Sprintf(" curl %s/bundle/1\n", baseURL))
233
234
sb.WriteString(fmt.Sprintf(" curl %s/data/42 -o 000042.jsonl.zst\n", baseURL))
234
235
sb.WriteString(fmt.Sprintf(" curl %s/jsonl/1\n", baseURL))
236
+
sb.WriteString(fmt.Sprintf(" curl %s/op/0\n", baseURL))
235
237
236
238
if s.config.EnableWebSocket {
237
239
sb.WriteString(fmt.Sprintf(" websocat %s/ws\n", wsURL))
···
601
603
sendJSON(w, 200, auditLog)
602
604
}
603
605
}
606
+
607
+
// handleOperation gets a single operation with detailed timing headers
608
+
func (s *Server) handleOperation() http.HandlerFunc {
609
+
return func(w http.ResponseWriter, r *http.Request) {
610
+
pointer := r.PathValue("pointer")
611
+
612
+
// Parse pointer format: "bundle:position" or global position
613
+
bundleNum, position, err := parseOperationPointer(pointer)
614
+
if err != nil {
615
+
sendJSON(w, 400, map[string]string{"error": err.Error()})
616
+
return
617
+
}
618
+
619
+
// Validate position range
620
+
if position < 0 || position >= types.BUNDLE_SIZE {
621
+
sendJSON(w, 400, map[string]string{
622
+
"error": fmt.Sprintf("Position must be 0-%d", types.BUNDLE_SIZE-1),
623
+
})
624
+
return
625
+
}
626
+
627
+
// Time the entire request
628
+
totalStart := time.Now()
629
+
630
+
// Time the operation load
631
+
loadStart := time.Now()
632
+
op, err := s.manager.LoadOperation(r.Context(), bundleNum, position)
633
+
loadDuration := time.Since(loadStart)
634
+
635
+
if err != nil {
636
+
if strings.Contains(err.Error(), "not in index") ||
637
+
strings.Contains(err.Error(), "not found") {
638
+
sendJSON(w, 404, map[string]string{"error": "Operation not found"})
639
+
} else {
640
+
sendJSON(w, 500, map[string]string{"error": err.Error()})
641
+
}
642
+
return
643
+
}
644
+
645
+
totalDuration := time.Since(totalStart)
646
+
647
+
// Calculate global position
648
+
globalPos := (bundleNum * types.BUNDLE_SIZE) + position
649
+
650
+
// Calculate operation age
651
+
opAge := time.Since(op.CreatedAt)
652
+
653
+
// Set response headers with useful metadata
654
+
setOperationHeaders(w, op, bundleNum, position, globalPos, loadDuration, totalDuration, opAge)
655
+
656
+
// Send raw JSON if available (faster, preserves exact format)
657
+
if len(op.RawJSON) > 0 {
658
+
w.Header().Set("Content-Type", "application/json")
659
+
w.Write(op.RawJSON)
660
+
} else {
661
+
sendJSON(w, 200, op)
662
+
}
663
+
}
664
+
}
665
+
666
+
// parseOperationPointer parses pointer in format "bundle:position" or global position
667
+
func parseOperationPointer(pointer string) (bundleNum, position int, err error) {
668
+
// Check if it's the "bundle:position" format
669
+
if strings.Contains(pointer, ":") {
670
+
parts := strings.Split(pointer, ":")
671
+
if len(parts) != 2 {
672
+
return 0, 0, fmt.Errorf("invalid pointer format: use 'bundle:position' or global position")
673
+
}
674
+
675
+
bundleNum, err = strconv.Atoi(parts[0])
676
+
if err != nil {
677
+
return 0, 0, fmt.Errorf("invalid bundle number: %w", err)
678
+
}
679
+
680
+
position, err = strconv.Atoi(parts[1])
681
+
if err != nil {
682
+
return 0, 0, fmt.Errorf("invalid position: %w", err)
683
+
}
684
+
685
+
if bundleNum < 1 {
686
+
return 0, 0, fmt.Errorf("bundle number must be >= 1")
687
+
}
688
+
689
+
return bundleNum, position, nil
690
+
}
691
+
692
+
// Parse as global position
693
+
globalPos, err := strconv.Atoi(pointer)
694
+
if err != nil {
695
+
return 0, 0, fmt.Errorf("invalid position: must be number or 'bundle:position' format")
696
+
}
697
+
698
+
if globalPos < 0 {
699
+
return 0, 0, fmt.Errorf("global position must be >= 0")
700
+
}
701
+
702
+
// Handle small numbers as shorthand for bundle 1
703
+
if globalPos < types.BUNDLE_SIZE {
704
+
return 1, globalPos, nil
705
+
}
706
+
707
+
// Convert global position to bundle + position
708
+
bundleNum = globalPos / types.BUNDLE_SIZE
709
+
position = globalPos % types.BUNDLE_SIZE
710
+
711
+
// Minimum bundle number is 1
712
+
if bundleNum < 1 {
713
+
bundleNum = 1
714
+
}
715
+
716
+
return bundleNum, position, nil
717
+
}
718
+
719
+
// setOperationHeaders sets useful response headers
720
+
func setOperationHeaders(
721
+
w http.ResponseWriter,
722
+
op *plcclient.PLCOperation,
723
+
bundleNum, position, globalPos int,
724
+
loadDuration, totalDuration, opAge time.Duration,
725
+
) {
726
+
// === Location Information ===
727
+
w.Header().Set("X-Bundle-Number", fmt.Sprintf("%d", bundleNum))
728
+
w.Header().Set("X-Position", fmt.Sprintf("%d", position))
729
+
w.Header().Set("X-Global-Position", fmt.Sprintf("%d", globalPos))
730
+
w.Header().Set("X-Pointer", fmt.Sprintf("%d:%d", bundleNum, position))
731
+
732
+
// === Operation Metadata ===
733
+
w.Header().Set("X-Operation-DID", op.DID)
734
+
w.Header().Set("X-Operation-CID", op.CID)
735
+
w.Header().Set("X-Operation-Created", op.CreatedAt.Format(time.RFC3339))
736
+
w.Header().Set("X-Operation-Age-Seconds", fmt.Sprintf("%d", int(opAge.Seconds())))
737
+
738
+
// Nullification status
739
+
if op.IsNullified() {
740
+
w.Header().Set("X-Operation-Nullified", "true")
741
+
if nullCID := op.GetNullifyingCID(); nullCID != "" {
742
+
w.Header().Set("X-Operation-Nullified-By", nullCID)
743
+
}
744
+
} else {
745
+
w.Header().Set("X-Operation-Nullified", "false")
746
+
}
747
+
748
+
// === Size Information ===
749
+
if len(op.RawJSON) > 0 {
750
+
w.Header().Set("X-Operation-Size", fmt.Sprintf("%d", len(op.RawJSON)))
751
+
}
752
+
753
+
// === Performance Metrics (in milliseconds with precision) ===
754
+
w.Header().Set("X-Load-Time-Ms", fmt.Sprintf("%.3f", float64(loadDuration.Microseconds())/1000.0))
755
+
w.Header().Set("X-Total-Time-Ms", fmt.Sprintf("%.3f", float64(totalDuration.Microseconds())/1000.0))
756
+
757
+
// === Caching Hints ===
758
+
// Set cache control (operations are immutable once bundled)
759
+
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
760
+
w.Header().Set("ETag", op.CID) // CID is perfect for ETag
761
+
}
+1
server/server.go
+1
server/server.go
···
69
69
mux.HandleFunc("GET /bundle/{number}", s.handleBundle())
70
70
mux.HandleFunc("GET /data/{number}", s.handleBundleData())
71
71
mux.HandleFunc("GET /jsonl/{number}", s.handleBundleJSONL())
72
+
mux.HandleFunc("GET /op/{pointer}", s.handleOperation())
72
73
mux.HandleFunc("GET /status", s.handleStatus())
73
74
mux.HandleFunc("GET /debug/memory", s.handleDebugMemory())
74
75