Skip to content

Commit ca2cbab

Browse files
authored
Merge pull request #9 from i5heu/stream-prototype
Make stream-prototype the main state
2 parents f85c485 + 8eeb6eb commit ca2cbab

Some content is hidden

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

49 files changed

+12268
-176
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
main
2-
*.log
2+
*.log
3+
data/

README.md

Lines changed: 120 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,130 @@
1-
[![Go Reference](https://pkg.go.dev/badge/github.com/i5heu/ouroboros-db.svg)](https://pkg.go.dev/github.com/i5heu/ouroboros-db) [![Tests](https://github.com/i5heu/OuroborosDB/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/i5heu/OuroborosDB/actions/workflows/go.yml) ![](https://github.com/i5heu/OuroborosDB/blob/gh-pages/.badges/main/coverage.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/i5heu/ouroboros-db)](https://goreportcard.com/report/github.com/i5heu/ouroboros-db) [![wakatime](https://wakatime.com/badge/github/i5heu/ouroboros-db.svg)](https://wakatime.com/badge/github/i5heu/ouroboros-db)
2-
3-
<p align="center">
4-
<img src=".media/logo.jpeg" width="300">
5-
</p>
1+
# OuroborosDB
62

7-
⚠️ This Project is still in development and not ready for production use. ⚠️
3+
[![Go Reference](https://pkg.go.dev/badge/github.com/i5heu/ouroboros-db.svg)](https://pkg.go.dev/github.com/i5heu/ouroboros-db) [![Tests](https://github.com/i5heu/OuroborosDB/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/i5heu/OuroborosDB/actions/workflows/go.yml) ![Coverage badge](https://github.com/i5heu/OuroborosDB/blob/gh-pages/.badges/main/coverage.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/i5heu/ouroboros-db)](https://goreportcard.com/report/github.com/i5heu/ouroboros-db) [![wakatime](https://wakatime.com/badge/github/i5heu/ouroboros-db.svg)](https://wakatime.com/badge/github/i5heu/ouroboros-db)
84

9-
# OuroborosDB
5+
![OuroborosDB logo](.media/logo.jpeg)
6+
7+
⚠️ This Project is still in development and not ready for production use. ⚠️
108

119
## Name and Logo
1210

1311
The name "OuroborosDB" is derived from the ancient symbol "Ouroboros," a representation of cyclical events, continuity, and endless return. Historically, it's been a potent symbol across various cultures, signifying the eternal cycle of life, death, and rebirth. In the context of this database, the Ouroboros symbolizes the perpetual preservation and renewal of data. While the traditional Ouroboros depicts a serpent consuming its tail, our version deviates, hinting at both reverence for historical cycles and the importance of continuous adaptation in the face of change.
1412

1513
## License
14+
1615
OuroborosDB (c) 2024 Mia Heidenstedt and contributors
17-
16+
1817
SPDX-License-Identifier: AGPL-3.0
18+
19+
## HTTP API
20+
21+
The reference server in `cmd/server` now exposes a lightweight HTTP API on port 8083. Start the binary and the REST endpoints below become available.
22+
23+
### `POST /data`
24+
25+
Create a new stored object. The body must be JSON with the payload content encoded as base64. Reed-Solomon parameters default to 4 data shards and 2 parity shards when omitted. Provide an optional `mime_type` (<=255 bytes) to mark binary data; absence of a MIME type treats the payload as UTF-8 text.
26+
27+
```json
28+
{
29+
"content": "aGVsbG8gd29ybGQ=",
30+
"reed_solomon_shards": 4,
31+
"reed_solomon_parity_shards": 2,
32+
"mime_type": "application/octet-stream"
33+
}
34+
```
35+
36+
Successful requests respond with the SHA-512 key of the stored data:
37+
38+
```json
39+
{
40+
"key": "<hex-encoded-hash>"
41+
}
42+
```
43+
44+
### `GET /data`
45+
46+
List all stored keys returned as hexadecimal-encoded hashes:
47+
48+
```json
49+
{
50+
"keys": ["<hex-encoded-hash>" ]
51+
}
52+
```
53+
54+
### Try it
55+
56+
```bash
57+
curl -X POST http://localhost:8083/data \
58+
-H 'Content-Type: application/json' \
59+
-d '{"content":"aGVsbG8=","reed_solomon_shards":4,"reed_solomon_parity_shards":2}'
60+
61+
curl http://localhost:8083/data
62+
```
63+
64+
### `GET /data/{key}`
65+
66+
Retrieve a stored entry by its hexadecimal hash. The response includes a base64 encoded payload, the inferred text flag, and the MIME type (if supplied when writing).
67+
68+
```json
69+
{
70+
"key": "<hex-encoded-hash>",
71+
"content": "aGVsbG8gd29ybGQ=",
72+
"is_text": true,
73+
"mime_type": ""
74+
}
75+
```
76+
77+
### Payload header format
78+
79+
Stored objects reserve the first 256 bytes for metadata:
80+
81+
- Byte `0`: the most significant bit (MSB) marks text payloads. When set (`1`), the payload is treated as UTF-8 text and no MIME is stored.
82+
- Bytes `1-255`: when the MSB is `0`, these bytes contain an ASCII/UTF-8 MIME type string padded with zeros. Empty or whitespace-only MIME values fall back to text handling.
83+
- Remaining bytes: the original payload content.
84+
85+
This layout allows fast retrieval of text/binary hints without additional metadata calls.
86+
87+
88+
## Development Notes
89+
90+
### Legend
91+
92+
#### Annotation legend for function comments:
93+
To indicate the correctness and safety of the logic of functions, the following annotations are used in comments directly after the function definitions (See examples below):
94+
95+
- `// A` - Function and was written by **AI** and was not reviewed by a **human**.
96+
- `// AP` - Function was written by **AI** and was reviewed but the **human** has found a potential issue which the **human** marked with a `// TODO ` comment.
97+
- `// AC` - Function was written by **AI** and was reviewed and approved by a **human** that has medium confidence in the correctness and safety of the logic.
98+
- `// H` - Function was written by a **human**
99+
- `// HP` - Function was written by a **human** but the **human** has found a potential issue which the **human** marked with a `// TODO ` comment.
100+
- `// HC` - A **human** comprehended the logic of th function in all its dimensions and is confident about its correctness and safety.
101+
102+
If the function has a higher risk profile (e.g., involves complex algorithms, security-sensitive operations, or critical data handling), a `P` prefix is added for `Priority`:
103+
104+
**All `P` function must be brought to `PHC` status before a production release.**
105+
106+
We add the indicators directly after the function declaration, although it is normally not common practice in Go, because it makes it easier to see the status of the function for most editors as they show use sticky function declaration.
107+
108+
Examples:
109+
```go
110+
111+
// This function does X, Y, and Z.
112+
func exampleFunction() { // A
113+
// Function is low risk and was written by AI and not reviewed by a human.
114+
}
115+
116+
// This function does X, Y, and Z.
117+
func exampleFunction() { // HC
118+
// Function is low risk and was comprehended by a human who is confident about its correctness and safety.
119+
}
120+
121+
// This function performs critical operations X, Y, and Z has some funky stuff going on.
122+
func criticalFunction() { // PAP
123+
// Function is high risk and was comprehended by a human who is confident about its correctness and safety.
124+
}
125+
126+
// This function performs critical operations X, Y, and Z.
127+
func criticalFunction() { // PHC
128+
// Function is high risk and was comprehended by a human who is confident about its correctness and safety.
129+
}
130+
```

apiServer/apiServer.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package apiServer
2+
3+
import (
4+
"log/slog"
5+
"net/http"
6+
7+
ouroboros "github.com/i5heu/ouroboros-db"
8+
"github.com/i5heu/ouroboros-db/browserCrypt"
9+
)
10+
11+
const (
12+
defaultDataShards = 4
13+
defaultParityShards = 2
14+
)
15+
16+
type Server struct {
17+
mux *http.ServeMux
18+
db *ouroboros.OuroborosDB
19+
log *slog.Logger
20+
auth AuthFunc
21+
authStore browserCrypt.AuthStore
22+
}
23+
24+
func New(db *ouroboros.OuroborosDB, opts ...Option) *Server { // A
25+
s := &Server{
26+
mux: http.NewServeMux(),
27+
db: db,
28+
log: slog.Default(),
29+
auth: defaultAuth,
30+
authStore: browserCrypt.AuthStore{
31+
OTK: make(map[string]browserCrypt.BrowserKey),
32+
},
33+
}
34+
35+
for _, opt := range opts {
36+
opt(s)
37+
}
38+
39+
s.routes()
40+
return s
41+
}
42+
43+
func (s *Server) routes() { // AC
44+
s.mux.HandleFunc("POST /data", s.handleCreate)
45+
s.mux.HandleFunc("GET /data/{key}", s.handleGet)
46+
s.mux.HandleFunc("GET /data/{key}/children", s.handleChildren)
47+
s.mux.HandleFunc("GET /data", s.handleList)
48+
s.mux.HandleFunc("POST /data/bulk", s.handleBulkData)
49+
s.mux.HandleFunc("GET /meta/threads", s.handleThreadSummaries)
50+
s.mux.HandleFunc("GET /meta/thread/{key}/stream", s.handleThreadNodeStream)
51+
s.mux.HandleFunc("GET /authProcess", s.handleAuthProcess)
52+
s.mux.HandleFunc("POST /authProcess", s.handleAuthProcess)
53+
}
54+
55+
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // AC
56+
origin := r.Header.Get("Origin")
57+
if origin == "" {
58+
origin = "*"
59+
} else {
60+
w.Header().Set("Vary", "Origin")
61+
}
62+
63+
if origin != "" {
64+
w.Header().Set("Access-Control-Allow-Origin", origin)
65+
}
66+
67+
// Use explicit, fixed CORS header lists so preflight responses are cacheable
68+
// and not dynamically varied by the incoming Access-Control-Request-Headers header.
69+
// Keep headers aligned with what the client sends: Content-Type + Accept + our
70+
// custom X-Auth headers and internal X-Ouroboros response/exposed headers.
71+
allowedHeaders := "Content-Type, Accept, X-Auth-Token, X-Auth-Nonce, X-Auth-KeyHash-Base64"
72+
w.Header().Set("Access-Control-Allow-Headers", allowedHeaders)
73+
// Allow caching of preflight responses so repeated identical requests don't cause
74+
// a preflight for every request. 86400s == 24 hours.
75+
w.Header().Set("Access-Control-Max-Age", "86400")
76+
w.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
77+
// If the API ever uses browser credentials (cookies) set to true. Current
78+
// client uses custom headers, so credentials is not required; enable as false for now.
79+
// If credentials are used, then Access-Control-Allow-Origin must not be '*'.
80+
// w.Header().Set("Access-Control-Allow-Credentials", "true")
81+
w.Header().Set(
82+
"Access-Control-Expose-Headers",
83+
"Content-Type, Content-Length, X-Ouroboros-Key, X-Ouroboros-Mime, X-Ouroboros-Is-Text, X-Ouroboros-Parent, X-Ouroboros-Children, X-Ouroboros-Created-At",
84+
)
85+
86+
if r.Method == http.MethodOptions {
87+
w.WriteHeader(http.StatusNoContent)
88+
return
89+
}
90+
91+
if r.URL.Path != "/authProcess" {
92+
if err := s.auth(r, s.db); err != nil {
93+
s.log.Warn("authentication failed", "error", err)
94+
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
95+
return
96+
}
97+
}
98+
99+
s.mux.ServeHTTP(w, r)
100+
}

0 commit comments

Comments
 (0)