project renamed

This commit is contained in:
d
2026-06-06 19:20:38 +03:00
parent 07d617b4a6
commit 8457f34fa5
19 changed files with 57 additions and 40 deletions
+5 -5
View File
@@ -1,8 +1,8 @@
## MESH ## M8SH
What email should have evolved into. What email should have evolved into.
`MESH` is a decentralized network, using the well-known paradigm of email addresses for user identification, but offering much more: search, posts, music, reels, videos, streams, messenger, cloud storage, and many other functions — all powered by a decentralized network. `M8SH` is a decentralized network, using the well-known paradigm of email addresses for user identification, but offering much more: search, posts, music, reels, videos, streams, messenger, cloud storage, and many other functions — all powered by a decentralized network.
The main project goal is to become a replacement for existing monopolized data markets, to save our freedom to own, distribute, and sell different forms of data. Rules of distribution and monetization for different pieces of content should be decided only by community. The main project goal is to become a replacement for existing monopolized data markets, to save our freedom to own, distribute, and sell different forms of data. Rules of distribution and monetization for different pieces of content should be decided only by community.
@@ -10,11 +10,11 @@ The main project goal is to become a replacement for existing monopolized data m
#### How does this work? #### How does this work?
Once a user downloads the mesh client, it asks them to pick a username and home domain. The client generates a keypair, sends the public key to the server, and receives an email address like neo@mesh.org. No passwords. No sessions. No KYC. Pure cryptography. Once a user downloads the m8sh client, it asks them to pick a username and home domain. The client generates a keypair, sends the public key to the server, and receives an email address like neo@m8sh.org. No passwords. No sessions. No KYC. Pure cryptography.
When performing any action on another domain — liking, subscribing, commenting — the client signs it with the private key. The signature is verified by any node against the user's home server. No central auth. Just crypto. When performing any action on another domain — liking, subscribing, commenting — the client signs it with the private key. The signature is verified by any node against the user's home server. No central auth. Just crypto.
Public content is accessible to search engines via regular websites. Each mesh server has an integrated search engine and can search across other instances. Content is fingerprinted; copies are detected and never shown to users. If content exceeds 50 MB, it is redistributed via BitTorrent to offload the server. Public content is accessible to search engines via regular websites. Each m8sh server has an integrated search engine and can search across other instances. Content is fingerprinted; copies are detected and never shown to users. If content exceeds 50 MB, it is redistributed via BitTorrent to offload the server.
Servers are not saving payloads for external likes/comments/subscriptions - only lightweight references to other nodes. When user likes/subscribes/comments specific content - payload and signature are saved on his domestic server, while external server only updates minimal info in reference. Servers are not saving payloads for external likes/comments/subscriptions - only lightweight references to other nodes. When user likes/subscribes/comments specific content - payload and signature are saved on his domestic server, while external server only updates minimal info in reference.
@@ -28,4 +28,4 @@ Monetization is specified at the protocol level to protect all network participa
Project is in active development. Repository contains server implementation written in go. Contributions are always welcome. Project is in active development. Repository contains server implementation written in go. Contributions are always welcome.
Vulnerabilities and security findings should be reported to d1nch8g@chx.su, via email or mesh messenger. Vulnerabilities and security findings should be reported to d@m8sh.su, via email or m8sh messenger.
+9 -14
View File
@@ -46,7 +46,6 @@ type User struct {
type Subscriptions interface { type Subscriptions interface {
Subscribe(user, target, domain string, sig []byte) error Subscribe(user, target, domain string, sig []byte) error
Unsubscribe(user, target, domain string) error Unsubscribe(user, target, domain string) error
OfDomestic(user string, limit, offset int) ([]SubscriptionToForeign, error) OfDomestic(user string, limit, offset int) ([]SubscriptionToForeign, error)
ToForeign(target, domain string, limit, offset int) ([]SubscriptionOfDomestic, error) ToForeign(target, domain string, limit, offset int) ([]SubscriptionOfDomestic, error)
@@ -149,11 +148,9 @@ type Post struct {
type Reactions interface { type Reactions interface {
SetAllowed(author string, date time.Time, emojis []string) error SetAllowed(author string, date time.Time, emojis []string) error
Allowed(author string, date time.Time) ([]string, error) GetAllowed(author string, date time.Time) ([]string, error)
Add(user, author, domain string, date time.Time, emoji string, sig []byte) error Add(user, author, domain string, date time.Time, emoji string, sig []byte) error
Remove(user, author, domain string, date time.Time, emoji string) error Remove(user, author, domain string, date time.Time, emoji string) error
OfUser(user string, limit, offset int) ([]ReactionToForeign, error) OfUser(user string, limit, offset int) ([]ReactionToForeign, error)
ToContent(author, domain string, date time.Time, limit, offset int) ([]ReactionOfDomestic, error) ToContent(author, domain string, date time.Time, limit, offset int) ([]ReactionOfDomestic, error)
@@ -179,17 +176,15 @@ type ReactionCounter struct {
Count uint64 `json:"count"` Count uint64 `json:"count"`
} }
// type Comments interface { type Comments interface {
// Enable(author string, date time.Time) error Enable(author string, date time.Time) error
// Disable(author string, date time.Time) error Disable(author string, date time.Time) error
}
// // Add(user, auth) type Comment struct {
// } Body string `json:"body" gob:"1"`
Signature []byte `json:"signature" gob:"2"`
// type Comment struct { }
// Body string `json:"body" gob:"1"`
// Signature []byte `json:"signature" gob:"2"`
// }
// Next modules to support: // Next modules to support:
// //
+1 -1
View File
@@ -6,9 +6,9 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
) )
type chat struct { type chat struct {
+1 -1
View File
@@ -4,8 +4,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
) )
func TestChat_Put(t *testing.T) { func TestChat_Put(t *testing.T) {
+1 -1
View File
@@ -7,9 +7,9 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
) )
type email struct { type email struct {
+1 -1
View File
@@ -4,8 +4,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
) )
func TestEmail_Put(t *testing.T) { func TestEmail_Put(t *testing.T) {
+1 -1
View File
@@ -5,10 +5,10 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
) )
type files struct { type files struct {
+1 -1
View File
@@ -5,9 +5,9 @@ import (
"os" "os"
"testing" "testing"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
) )
func openDB(t *testing.T) (*DB, func()) { func openDB(t *testing.T) (*DB, func()) {
+1 -1
View File
@@ -4,10 +4,10 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/opt"
"m8sh.su/x/m8sh/database"
) )
const ( const (
+1 -1
View File
@@ -6,9 +6,9 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
) )
type posts struct { type posts struct {
+1 -1
View File
@@ -4,8 +4,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
) )
func TestPosts_Create(t *testing.T) { func TestPosts_Create(t *testing.T) {
+2 -2
View File
@@ -8,9 +8,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
) )
type reactions struct { type reactions struct {
@@ -34,7 +34,7 @@ func (r *reactions) SetAllowed(owner string, date time.Time, emojis []string) er
return nil return nil
} }
func (r *reactions) Allowed(owner string, date time.Time) ([]string, error) { func (r *reactions) GetAllowed(owner string, date time.Time) ([]string, error) {
k := key(owner, prefixReactionsAllowed, formatTime(date)) k := key(owner, prefixReactionsAllowed, formatTime(date))
data, err := r.db.Get(k, nil) data, err := r.db.Get(k, nil)
+5 -5
View File
@@ -4,8 +4,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
) )
func TestReactions_SetAllowed(t *testing.T) { func TestReactions_SetAllowed(t *testing.T) {
@@ -44,18 +44,18 @@ func TestReactions_Allowed(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
t.Run("get allowed emojis", func(t *testing.T) { t.Run("get allowed emojis", func(t *testing.T) {
got, err := reactions.Allowed(owner, date) got, err := reactions.GetAllowed(owner, date)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, []string{"👍", "🔥"}, got) require.Equal(t, []string{"👍", "🔥"}, got)
}) })
t.Run("get non-existing allowed", func(t *testing.T) { t.Run("get non-existing allowed", func(t *testing.T) {
_, err := reactions.Allowed("unknown", date) _, err := reactions.GetAllowed("unknown", date)
require.ErrorIs(t, err, database.ErrNotFound) require.ErrorIs(t, err, database.ErrNotFound)
}) })
t.Run("get allowed for different time", func(t *testing.T) { t.Run("get allowed for different time", func(t *testing.T) {
_, err := reactions.Allowed(owner, date.Add(time.Hour)) _, err := reactions.GetAllowed(owner, date.Add(time.Hour))
require.ErrorIs(t, err, database.ErrNotFound) require.ErrorIs(t, err, database.ErrNotFound)
}) })
} }
@@ -348,7 +348,7 @@ func TestReactions_AfterClose(t *testing.T) {
require.NotPanics(t, func() { require.NotPanics(t, func() {
reactions.SetAllowed("masha", time.Now(), []string{"👍"}) reactions.SetAllowed("masha", time.Now(), []string{"👍"})
reactions.Allowed("masha", time.Now()) reactions.GetAllowed("masha", time.Now())
reactions.Add("masha", "bob", "d2.io", time.Now(), "👍", nil) reactions.Add("masha", "bob", "d2.io", time.Now(), "👍", nil)
reactions.Remove("masha", "bob", "d2.io", time.Now(), "👍") reactions.Remove("masha", "bob", "d2.io", time.Now(), "👍")
reactions.OfUser("masha", 10, 0) reactions.OfUser("masha", 10, 0)
+1 -1
View File
@@ -7,9 +7,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
) )
type counter struct { type counter struct {
+1 -1
View File
@@ -4,9 +4,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
) )
func TestSubscriptions_Subscribe(t *testing.T) { func TestSubscriptions_Subscribe(t *testing.T) {
+1 -1
View File
@@ -6,8 +6,8 @@ import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"m8sh.su/x/m8sh/database"
) )
type users struct { type users struct {
+1 -1
View File
@@ -4,9 +4,9 @@ import (
"encoding/binary" "encoding/binary"
"testing" "testing"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
) )
func TestUsers_Create(t *testing.T) { func TestUsers_Create(t *testing.T) {
+1 -1
View File
@@ -1,4 +1,4 @@
module github.com/d1nch8g/mesh module m8sh.su/x/m8sh
go 1.26.2 go 1.26.2
+22
View File
@@ -0,0 +1,22 @@
<svg width="400" height="400" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" />
<g stroke="#0052a3" stroke-width="22" stroke-linecap="round" stroke-linejoin="round">
<line x1="60" y1="115" x2="200" y2="75" />
<line x1="200" y1="75" x2="340" y2="135" />
<line x1="60" y1="115" x2="90" y2="300" />
<line x1="200" y1="75" x2="230" y2="240" />
<line x1="340" y1="135" x2="230" y2="240" />
<line x1="230" y1="240" x2="290" y2="355" />
<line x1="90" y1="300" x2="290" y2="355" />
</g>
<g fill="#0052a3">
<circle cx="60" cy="115" r="42" />
<circle cx="200" cy="75" r="55" />
<circle cx="340" cy="135" r="48" />
<circle cx="90" cy="300" r="50" />
<circle cx="230" cy="240" r="40" />
<circle cx="290" cy="355" r="38" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 830 B