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.
`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.
@@ -10,11 +10,11 @@ The main project goal is to become a replacement for existing monopolized data m
#### 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.
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.
@@ -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.
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 {
Subscribe(user, target, domain string, sig []byte) error
Unsubscribe(user, target, domain string) error
OfDomestic(user string, limit, offset int) ([]SubscriptionToForeign, error)
ToForeign(target, domain string, limit, offset int) ([]SubscriptionOfDomestic, error)
@@ -149,11 +148,9 @@ type Post struct {
type Reactions interface {
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
Remove(user, author, domain string, date time.Time, emoji string) error
OfUser(user string, limit, offset int) ([]ReactionToForeign, error)
ToContent(author, domain string, date time.Time, limit, offset int) ([]ReactionOfDomestic, error)
@@ -179,17 +176,15 @@ type ReactionCounter struct {
Count uint64 `json:"count"`
}
// type Comments interface {
// Enable(author string, date time.Time) error
// Disable(author string, date time.Time) error
type Comments interface {
Enable(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:
//
+1 -1
View File
@@ -6,9 +6,9 @@ import (
"fmt"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
)
type chat struct {
+1 -1
View File
@@ -4,8 +4,8 @@ import (
"testing"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
)
func TestChat_Put(t *testing.T) {
+1 -1
View File
@@ -7,9 +7,9 @@ import (
"fmt"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
)
type email struct {
+1 -1
View File
@@ -4,8 +4,8 @@ import (
"testing"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
)
func TestEmail_Put(t *testing.T) {
+1 -1
View File
@@ -5,10 +5,10 @@ import (
"fmt"
"io"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
)
type files struct {
+1 -1
View File
@@ -5,9 +5,9 @@ import (
"os"
"testing"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
)
func openDB(t *testing.T) (*DB, func()) {
+1 -1
View File
@@ -4,10 +4,10 @@ import (
"fmt"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt"
"m8sh.su/x/m8sh/database"
)
const (
+1 -1
View File
@@ -6,9 +6,9 @@ import (
"fmt"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
)
type posts struct {
+1 -1
View File
@@ -4,8 +4,8 @@ import (
"testing"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
)
func TestPosts_Create(t *testing.T) {
+2 -2
View File
@@ -8,9 +8,9 @@ import (
"sync"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
)
type reactions struct {
@@ -34,7 +34,7 @@ func (r *reactions) SetAllowed(owner string, date time.Time, emojis []string) er
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))
data, err := r.db.Get(k, nil)
+5 -5
View File
@@ -4,8 +4,8 @@ import (
"testing"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
)
func TestReactions_SetAllowed(t *testing.T) {
@@ -44,18 +44,18 @@ func TestReactions_Allowed(t *testing.T) {
require.NoError(t, err)
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.Equal(t, []string{"👍", "🔥"}, got)
})
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)
})
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)
})
}
@@ -348,7 +348,7 @@ func TestReactions_AfterClose(t *testing.T) {
require.NotPanics(t, func() {
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.Remove("masha", "bob", "d2.io", time.Now(), "👍")
reactions.OfUser("masha", 10, 0)
+1 -1
View File
@@ -7,9 +7,9 @@ import (
"sync"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
"m8sh.su/x/m8sh/database"
)
type counter struct {
+1 -1
View File
@@ -4,9 +4,9 @@ import (
"testing"
"time"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
)
func TestSubscriptions_Subscribe(t *testing.T) {
+1 -1
View File
@@ -6,8 +6,8 @@ import (
"encoding/gob"
"fmt"
"github.com/d1nch8g/mesh/database"
"github.com/syndtr/goleveldb/leveldb"
"m8sh.su/x/m8sh/database"
)
type users struct {
+1 -1
View File
@@ -4,9 +4,9 @@ import (
"encoding/binary"
"testing"
"github.com/d1nch8g/mesh/database"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
)
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
+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