Searching txt.sour.is

Twts matching #typing
Sort by: Newest, Oldest, Most Relevant
In-reply-to » Trying to wrap my head around webfinger..

so in effect it would look something like this:

---
subject: acct:me@sour.is
aliases:
  - salty:me@sour.is
  - yarn:xuu@ev.sour.is
  - status:xuu@chaos.social
  - mailto:me@sour.is
---
subject: salty:me@sour.is
aliases:
  - acct:me@sour.is
links:
  - rel:    self
    type:   application/json+salty
    href:   https://ev.sour.is/inbox/01GAEMKXYJ4857JQP1MJGD61Z5
    properties:
        "http://salty.im/ns/nick":    xuu
        "http://salty.im/ns/display": Jon Lundy
        "http://salty.im/ns/pubkey":     kex140fwaena9t0mrgnjeare5zuknmmvl0vc7agqy5yr938vusxfh9ys34vd2p
---
subject: yarn:xuu@ev.sour.is
links:
  - rel: https://txt.sour.is/user/xuu
    properties:
        "https://sour.is/rel/redirect": https://txt.sour.is/.well-known/webfinger?resource=acct%3Axuu%40txt.sour.is
---    
subject: status:xuu@chaos.social
links:
   - rel: http://joinmastodon.org#xuu%40chaos.social
     properties:
        "https://sour.is/rel/redirect": https://chaos.social/.well-known/webfinger?resource=acct%3Axuu%40chaos.social
---
subject: mailto:me@sour.is
...

⤋ Read More
In-reply-to » Trying to wrap my head around webfinger..

so in effect it would look something like this:

---
subject: acct:me@sour.is
aliases:
  - salty:me@sour.is
  - yarn:xuu@ev.sour.is
  - status:xuu@chaos.social
  - mailto:me@sour.is
---
subject: salty:me@sour.is
aliases:
  - acct:me@sour.is
links:
  - rel:    self
    type:   application/json+salty
    href:   https://ev.sour.is/inbox/01GAEMKXYJ4857JQP1MJGD61Z5
    properties:
        "http://salty.im/ns/nick":    xuu
        "http://salty.im/ns/display": Jon Lundy
        "http://salty.im/ns/pubkey":     kex140fwaena9t0mrgnjeare5zuknmmvl0vc7agqy5yr938vusxfh9ys34vd2p
---
subject: yarn:xuu@ev.sour.is
links:
  - rel: https://txt.sour.is/user/xuu
    properties:
        "https://sour.is/rel/redirect": https://txt.sour.is/.well-known/webfinger?resource=acct%3Axuu%40txt.sour.is
---    
subject: status:xuu@chaos.social
links:
   - rel: http://joinmastodon.org#xuu%40chaos.social
     properties:
        "https://sour.is/rel/redirect": https://chaos.social/.well-known/webfinger?resource=acct%3Axuu%40chaos.social
---
subject: mailto:me@sour.is
...

⤋ Read More
In-reply-to » Trying to wrap my head around webfinger..

@xuu@txt.sour.is that doesn’t seem to fit the spirit of the spec, at least by my read (I could be wrong obv). The example on Wikipedia’s webfinger page,

{
	"subject": "acct:bob@example.com",
	"aliases": [
		"https://www.example.com/~bob/"
	],
	"properties": {
		"http://example.com/ns/role": "employee"
	},
	"links": [{
			"rel": "http://webfinger.example/rel/profile-page",
			"href": "https://www.example.com/~bob/"
		},
		{
			"rel": "http://webfinger.example/rel/businesscard",
			"href": "https://www.example.com/~bob/bob.vcf"
		}
	]
}

and then the comparison with how mastodon uses webfinger,

{
    "subject": "acct:Mastodon@mastodon.social",
    "aliases": [
        "https://mastodon.social/@Mastodon",
        "https://mastodon.social/users/Mastodon"
    ],
    "links": [
        {
            "rel": "http://webfinger.net/rel/profile-page",
            "type": "text/html",
            "href": "https://mastodon.social/@Mastodon"
        },
        {
            "rel": "self",
            "type": "application/activity+json",
            "href": "https://mastodon.social/users/Mastodon"
        },
        {
            "rel": "http://ostatus.org/schema/1.0/subscribe",
            "template": "https://mastodon.social/authorize_interaction?uri={uri}"
        }
    ]
}

suggests to me you want to leave the subject/acct bit as is (don’t add prefixes) and put extra information you care to include in the links section, where you’re free to define the rel URIs however you see fit. The notion here is that webfinger is offering a mapping from an account name to additional information about that account, so if anything you’d use a "subject": "acct:SALTY ACCOUNT_REPRESENTATION" line in the JSON to achieve what you’re saying if you don’t want to do that via links.

⤋ Read More
In-reply-to » Trying to wrap my head around webfinger..

@prologic@twtxt.net That was exactly my thought at first too. but what do we put as the rel for salty account? since it is decentralized we dont have a set URL for machines to key off. so for example take the standard response from okta:

# http GET https://example.okta.com/.well-known/webfinger  resource==acct:bob
{
    "links": [
        {
            "href": "https://example.okta.com/sso/idps/OKTA?login_hint=bob#",
            "properties": {
                "okta:idp:type": "OKTA"
            },
            "rel": "http://openid.net/specs/connect/1.0/issuer",
            "titles": {
                "und": "example"
            }
        }
    ],
    "subject": "acct:bob"
}

It gives one link that follows the OpenID login. So the details are specific to the subject acct:bob.

Mastodons response:

{
  "subject": "acct:xuu@chaos.social",
  "aliases": [
    "https://chaos.social/@xuu",
    "https://chaos.social/users/xuu"
  ],
  "links": [
    {
      "rel": "http://webfinger.net/rel/profile-page",
      "type": "text/html",
      "href": "https://chaos.social/@xuu"
    },
    {
      "rel": "self",
      "type": "application/activity+json",
      "href": "https://chaos.social/users/xuu"
    },
    {
      "rel": "http://ostatus.org/schema/1.0/subscribe"
    }
  ]
}

it supplies a profile page and a self which are both specific to that account.

⤋ Read More
In-reply-to » Trying to wrap my head around webfinger..

@prologic@twtxt.net That was exactly my thought at first too. but what do we put as the rel for salty account? since it is decentralized we dont have a set URL for machines to key off. so for example take the standard response from okta:

# http GET https://example.okta.com/.well-known/webfinger  resource==acct:bob
{
    "links": [
        {
            "href": "https://example.okta.com/sso/idps/OKTA?login_hint=bob#",
            "properties": {
                "okta:idp:type": "OKTA"
            },
            "rel": "http://openid.net/specs/connect/1.0/issuer",
            "titles": {
                "und": "example"
            }
        }
    ],
    "subject": "acct:bob"
}

It gives one link that follows the OpenID login. So the details are specific to the subject acct:bob.

Mastodons response:

{
  "subject": "acct:xuu@chaos.social",
  "aliases": [
    "https://chaos.social/@xuu",
    "https://chaos.social/users/xuu"
  ],
  "links": [
    {
      "rel": "http://webfinger.net/rel/profile-page",
      "type": "text/html",
      "href": "https://chaos.social/@xuu"
    },
    {
      "rel": "self",
      "type": "application/activity+json",
      "href": "https://chaos.social/users/xuu"
    },
    {
      "rel": "http://ostatus.org/schema/1.0/subscribe"
    }
  ]
}

it supplies a profile page and a self which are both specific to that account.

⤋ Read More
In-reply-to » Tutorial: Getting started with generics - The Go Programming Language -- Okay @xuu I quite like Go's generics now 🤣 After going through this myself I like the semantics and the syntax. I'm glad they did a lot of work on this to keep it simple to both understand and use (just like the rest of Go) 👌 Media #GoLang #Generics

@prologic@twtxt.net see where its used maybe that can help.
https://github.com/sour-is/ev/blob/main/app/peerfinder/http.go#L153

This is an upsert. So I pass a streamID which is like a globally unique id for the object. And then see how the type of the parameter in the function is used to infer the generic type. In the function it will create a new *Info and populate it from the datastore to pass to the function. The func will do its modifications and if it returns a nil error it will commit the changes.

The PA type contract ensures that the type fulfills the Aggregate interface and is a pointer to type at compile time.

⤋ Read More
In-reply-to » Tutorial: Getting started with generics - The Go Programming Language -- Okay @xuu I quite like Go's generics now 🤣 After going through this myself I like the semantics and the syntax. I'm glad they did a lot of work on this to keep it simple to both understand and use (just like the rest of Go) 👌 Media #GoLang #Generics

@prologic@twtxt.net see where its used maybe that can help.
https://github.com/sour-is/ev/blob/main/app/peerfinder/http.go#L153

This is an upsert. So I pass a streamID which is like a globally unique id for the object. And then see how the type of the parameter in the function is used to infer the generic type. In the function it will create a new *Info and populate it from the datastore to pass to the function. The func will do its modifications and if it returns a nil error it will commit the changes.

The PA type contract ensures that the type fulfills the Aggregate interface and is a pointer to type at compile time.

⤋ Read More
In-reply-to » Tutorial: Getting started with generics - The Go Programming Language -- Okay @xuu I quite like Go's generics now 🤣 After going through this myself I like the semantics and the syntax. I'm glad they did a lot of work on this to keep it simple to both understand and use (just like the rest of Go) 👌 Media #GoLang #Generics

one that i think is pretty interesting is building up dependent constraints. see here.. it accepts a type but requires the use of a pointer to type.

https://github.com/sour-is/ev/blob/main/pkg/es/es.go#L315-L325

⤋ Read More
In-reply-to » Tutorial: Getting started with generics - The Go Programming Language -- Okay @xuu I quite like Go's generics now 🤣 After going through this myself I like the semantics and the syntax. I'm glad they did a lot of work on this to keep it simple to both understand and use (just like the rest of Go) 👌 Media #GoLang #Generics

one that i think is pretty interesting is building up dependent constraints. see here.. it accepts a type but requires the use of a pointer to type.

https://github.com/sour-is/ev/blob/main/pkg/es/es.go#L315-L325

⤋ Read More

Ignite Realtime Blog: Spark 3.0.1 Released
The Ignite Realtime community is happy to announce the release of Spark 3.0.1 version.

This release contains mostly fixes. macOS now uses the default FlatLaf LaF. The user can also choose the type of tabs “scroll” as in Spark 3.0.0 or “wrap” as in Spark 2.X. See screenshot below. And also for some users, Spark not saved history.

Image


… ⌘ Read more

⤋ Read More
In-reply-to » I made a thing. Its a multi password type checker. Using the PHC string format we can identify a password hashing format from the prefix $name$ and then dispatch the hashing or checking to its specific format.

Hold up now, that example hash doesn’t have a $ prefix!

Well for this there is the option for a hash type to set itself as a fall through if a matching hash doesn’t exist. This is good for legacy password types that don’t follow the convention.

func (p *plainPasswd) ApplyPasswd(passwd *passwd.Passwd) {
	passwd.Register("plain", p)
	passwd.SetFallthrough(p)
}

https://github.com/sour-is/go-passwd/blob/main/passwd_test.go#L28-L31

⤋ Read More
In-reply-to » I made a thing. Its a multi password type checker. Using the PHC string format we can identify a password hashing format from the prefix $name$ and then dispatch the hashing or checking to its specific format.

Hold up now, that example hash doesn’t have a $ prefix!

Well for this there is the option for a hash type to set itself as a fall through if a matching hash doesn’t exist. This is good for legacy password types that don’t follow the convention.

func (p *plainPasswd) ApplyPasswd(passwd *passwd.Passwd) {
	passwd.Register("plain", p)
	passwd.SetFallthrough(p)
}

https://github.com/sour-is/go-passwd/blob/main/passwd_test.go#L28-L31

⤋ Read More
In-reply-to » I made a thing. Its a multi password type checker. Using the PHC string format we can identify a password hashing format from the prefix $name$ and then dispatch the hashing or checking to its specific format.

Here is an example of usage:

func Example() {
	pass := "my_pass"
	hash := "my_pass"

	pwd := passwd.New(
		&unix.MD5{}, // first is preferred type.
		&plainPasswd{},
	)

	_, err := pwd.Passwd(pass, hash)
	if err != nil {
		fmt.Println("fail: ", err)
	}

	// Check if we want to update.
	if !pwd.IsPreferred(hash) {
		newHash, err := pwd.Passwd(pass, "")
		if err != nil {
			fmt.Println("fail: ", err)
		}

		fmt.Println("new hash:", newHash)
	}

	// Output:
	//  new hash: $1$81ed91e1131a3a5a50d8a68e8ef85fa0
}

This shows how one would set a preferred hashing type and if the current version of ones password is not the preferred type updates it to enhance the security of the hashed password when someone logs in.

https://github.com/sour-is/go-passwd/blob/main/passwd_test.go#L33-L59

⤋ Read More
In-reply-to » I made a thing. Its a multi password type checker. Using the PHC string format we can identify a password hashing format from the prefix $name$ and then dispatch the hashing or checking to its specific format.

Here is an example of usage:

func Example() {
	pass := "my_pass"
	hash := "my_pass"

	pwd := passwd.New(
		&unix.MD5{}, // first is preferred type.
		&plainPasswd{},
	)

	_, err := pwd.Passwd(pass, hash)
	if err != nil {
		fmt.Println("fail: ", err)
	}

	// Check if we want to update.
	if !pwd.IsPreferred(hash) {
		newHash, err := pwd.Passwd(pass, "")
		if err != nil {
			fmt.Println("fail: ", err)
		}

		fmt.Println("new hash:", newHash)
	}

	// Output:
	//  new hash: $1$81ed91e1131a3a5a50d8a68e8ef85fa0
}

This shows how one would set a preferred hashing type and if the current version of ones password is not the preferred type updates it to enhance the security of the hashed password when someone logs in.

https://github.com/sour-is/go-passwd/blob/main/passwd_test.go#L33-L59

⤋ Read More
In-reply-to » JUHU! Finally! The new NAS runs. Oh boy what a process. First I had to restart and redow everything three times. Sometimes things are not sooo super obvious and then you really mess up. Who decided at Asustor that you cannot move home folders off of the Volume 1? And Why are the Asustor apps so bad? Beside that, the machine, the NAS, is really nice. Updraded to 16GB RAM and I finally have NGINX PROXY MANAGER running. Now I can setup all services with nice names!

@carsten@yarn.zn80.net what type of NAS? I just upgraded my oooold (~2008) Drobo to a Synology. I have been impressed with all the neat stuff it can do.

⤋ Read More
In-reply-to » JUHU! Finally! The new NAS runs. Oh boy what a process. First I had to restart and redow everything three times. Sometimes things are not sooo super obvious and then you really mess up. Who decided at Asustor that you cannot move home folders off of the Volume 1? And Why are the Asustor apps so bad? Beside that, the machine, the NAS, is really nice. Updraded to 16GB RAM and I finally have NGINX PROXY MANAGER running. Now I can setup all services with nice names!

@carsten@yarn.zn80.net what type of NAS? I just upgraded my oooold (~2008) Drobo to a Synology. I have been impressed with all the neat stuff it can do.

⤋ Read More
In-reply-to » Hmmm, after fixing my feeds to move the <author> from <entry>s to <feed>, Newsboat marked all old affected articles as unread. IDs were untouched, of course. Need to investigate that. Had something similar happen with another feed change I did some time ago. Can't remember what that was, though.

Great, last system update broke something, building from current master I get:

/usr/bin/ld: /lib/x86_64-linux-gnu/libm.so.6: unknown type [0x13] section `.relr.dyn'

What the heck!?

And it also appears that I’m not really able to reproduce this unread bug. It only kind of works a single time. And it has something to do with my config. Not sure what it is yet. I also noticed that the <updated> timestamps in the entries somehow shifted between the old and new feed. Da fuq!?

⤋ Read More

Announcing Docker Hub OCI Artifacts Support
We’re excited to announce that Docker Hub can now help you distribute any type of application artifact! You can now keep everything in one place without having to leverage multiple registries. Before today, you could only use Docker Hub to store and distribute container images — or artifacts usable by container runtimes. This became a […] ⌘ Read more

⤋ Read More

Gajim: Gajim 1.5.2
Gajim 1.5.2 brings another performance boost, better emojis, improvements for group chat moderators, and many bug fixes. Thank you for all your contributions!

What’s New

Generating performance profiles for Gajim revealed some bottlenecks in Gajim’s code. After fixing these, switching chats should now feel snappier than before.

Did you know that you can use shortcodes for typing emojis? Typing :+1 for example will ope … ⌘ Read more

⤋ Read More

Look, sir, I know Angua. She’s not the useless type. She doesn’t stand there and scream helplessly. She makes other people do that Look, sir, I know Angua. She’s not the useless… - Quotes from Terry Pratchett’s Discworld Books

⤋ Read More
In-reply-to » Today I found that Solarpunk is a thing: https://www.wikiwand.com/en/Solarpunk

@abucci@anthony.buc.ci Its not better than a Cat5e. I have had two versions of the device. The old ones were only 200Mbps i didn’t have the MAC issue but its like using an old 10baseT. The newer model can support 1Gbps on each port for a total bandwidth of 2Gbps.. i typically would see 400-500Mbps from my Wifi6 router. I am not sure if it was some type of internal timeout or being confused by switching between different wifi access points and seeing the mac on different sides.

Right now I have my wifi connected directly with a cat6e this gets me just under my providers 1.3G downlink. the only thing faster is plugging in directly.

MoCA is a good option, they have 2.5G models in the same price range as the 1G Powerline models BUT, only if you have the coax in wall already.. which puts you in the same spot if you don’t. You are for sure going to have an outlet in every room of the house by code.

⤋ Read More
In-reply-to » Today I found that Solarpunk is a thing: https://www.wikiwand.com/en/Solarpunk

@abucci@anthony.buc.ci Its not better than a Cat5e. I have had two versions of the device. The old ones were only 200Mbps i didn’t have the MAC issue but its like using an old 10baseT. The newer model can support 1Gbps on each port for a total bandwidth of 2Gbps.. i typically would see 400-500Mbps from my Wifi6 router. I am not sure if it was some type of internal timeout or being confused by switching between different wifi access points and seeing the mac on different sides.

Right now I have my wifi connected directly with a cat6e this gets me just under my providers 1.3G downlink. the only thing faster is plugging in directly.

MoCA is a good option, they have 2.5G models in the same price range as the 1G Powerline models BUT, only if you have the coax in wall already.. which puts you in the same spot if you don’t. You are for sure going to have an outlet in every room of the house by code.

⤋ Read More
In-reply-to » @prologic I think we could use deltachats new decentralising app format for it: https://delta.chat/en/2022-06-14-webxdcintro

Huh… Nope.

HTTP/1.1 200 OK
Content-Length: 407
Content-Type: text/calendar
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag
Permissions-Policy: interest-cohort=()
Content-Security-Policy: default-src 'none'; sandbox
Referrer-Policy: same-origin
Vary: Authorization


BEGIN:VCALENDAR
VERSION:2.0;2.0
PRODID:SandCal
CALSCALE:GREGORIAN
BEGIN:VEVENT
DTSTAMP:20220822T180903Z
UID:bb63bfbd-623e-4805-b11b-3181d96375e6
DTSTART;TZID=America/Chicago:20220827T000000
CREATED:20220822T180903Z
LAST-MODIFIED:20220822T180903Z
LOCATION:https://meet.jit.si/Yarn.social
SUMMARY:Yarn Call
RRULE:FREQ=WEEKLY
DTEND;TZID=America/Chicago:20220827T010000
END:VEVENT
END:VCALENDAR

⤋ Read More
In-reply-to » @prologic I think we could use deltachats new decentralising app format for it: https://delta.chat/en/2022-06-14-webxdcintro

Huh… Nope.

HTTP/1.1 200 OK
Content-Length: 407
Content-Type: text/calendar
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag
Permissions-Policy: interest-cohort=()
Content-Security-Policy: default-src 'none'; sandbox
Referrer-Policy: same-origin
Vary: Authorization


BEGIN:VCALENDAR
VERSION:2.0;2.0
PRODID:SandCal
CALSCALE:GREGORIAN
BEGIN:VEVENT
DTSTAMP:20220822T180903Z
UID:bb63bfbd-623e-4805-b11b-3181d96375e6
DTSTART;TZID=America/Chicago:20220827T000000
CREATED:20220822T180903Z
LAST-MODIFIED:20220822T180903Z
LOCATION:https://meet.jit.si/Yarn.social
SUMMARY:Yarn Call
RRULE:FREQ=WEEKLY
DTEND;TZID=America/Chicago:20220827T010000
END:VEVENT
END:VCALENDAR

⤋ Read More
In-reply-to » Progress! so i have moved into working on aggregates. Which are a grouping of events that replayed on an object set the current state of the object. I came up with this little bit of generic wonder.

(cont.)

Just to give some context on some of the components around the code structure.. I wrote this up around an earlier version of aggregate code. This generic bit simplifies things by removing the need of the Crud functions for each aggregate.

Domain Objects

A domain object can be used as an aggregate by adding the event.AggregateRoot struct and finish implementing event.Aggregate. The AggregateRoot implements logic for adding events after they are either Raised by a command or Appended by the eventstore Load or service ApplyFn methods. It also tracks the uncommitted events that are saved using the eventstore Save method.

type User struct {
  Identity string ```json:"identity"`

  CreatedAt time.Time

  event.AggregateRoot
}

// StreamID for the aggregate when stored or loaded from ES.
func (a *User) StreamID() string {
	return "user-" + a.Identity
}
// ApplyEvent to the aggregate state.
func (a *User) ApplyEvent(lis ...event.Event) {
	for _, e := range lis {
		switch e := e.(type) {
		case *UserCreated:
			a.Identity = e.Identity
			a.CreatedAt = e.EventMeta().CreatedDate
        /* ... */
		}
	}
}
Events

Events are applied to the aggregate. They are defined by adding the event.Meta and implementing the getter/setters for event.Event

type UserCreated struct {
	eventMeta event.Meta

	Identity string
}

func (c *UserCreated) EventMeta() (m event.Meta) {
	if c != nil {
		m = c.eventMeta
	}
	return m
}
func (c *UserCreated) SetEventMeta(m event.Meta) {
	if c != nil {
		c.eventMeta = m
	}
}
Reading Events from EventStore

With a domain object that implements the event.Aggregate the event store client can load events and apply them using the Load(ctx, agg) method.

// GetUser populates an user from event store.
func (rw *User) GetUser(ctx context.Context, userID string) (*domain.User, error) {
	user := &domain.User{Identity: userID}

	err := rw.es.Load(ctx, user)
	if err != nil {
		if err != nil {
			if errors.Is(err, eventstore.ErrStreamNotFound) {
				return user, ErrNotFound
			}
			return user, err
		}
		return nil, err
	}
	return user, err
}
OnX Commands

An OnX command will validate the state of the domain object can have the command performed on it. If it can be applied it raises the event using event.Raise() Otherwise it returns an error.

// OnCreate raises an UserCreated event to create the user.
// Note: The handler will check that the user does not already exsist.
func (a *User) OnCreate(identity string) error {
    event.Raise(a, &UserCreated{Identity: identity})
    return nil
}

// OnScored will attempt to score a task.
// If the task is not in a Created state it will fail.
func (a *Task) OnScored(taskID string, score int64, attributes Attributes) error {
	if a.State != TaskStateCreated {
		return fmt.Errorf("task expected created, got %s", a.State)
	}
	event.Raise(a, &TaskScored{TaskID: taskID, Attributes: attributes, Score: score})
	return nil
}
Crud Operations for OnX Commands

The following functions in the aggregate service can be used to perform creation and updating of aggregates. The Update function will ensure the aggregate exists, where the Create is intended for non-existent aggregates. These can probably be combined into one function.

// Create is used when the stream does not yet exist.
func (rw *User) Create(
  ctx context.Context,
  identity string,
  fn func(*domain.User) error,
) (*domain.User, error) {
	session, err := rw.GetUser(ctx, identity)
	if err != nil && !errors.Is(err, ErrNotFound) {
		return nil, err
	}

	if err = fn(session); err != nil {
		return nil, err
	}

	_, err = rw.es.Save(ctx, session)

	return session, err
}

// Update is used when the stream already exists.
func (rw *User) Update(
  ctx context.Context,
  identity string,
  fn func(*domain.User) error,
) (*domain.User, error) {
	session, err := rw.GetUser(ctx, identity)
	if err != nil {
		return nil, err
	}

	if err = fn(session); err != nil {
		return nil, err
	}

	_, err = rw.es.Save(ctx, session)
	return session, err
}

⤋ Read More
In-reply-to » Progress! so i have moved into working on aggregates. Which are a grouping of events that replayed on an object set the current state of the object. I came up with this little bit of generic wonder.

(cont.)

Just to give some context on some of the components around the code structure.. I wrote this up around an earlier version of aggregate code. This generic bit simplifies things by removing the need of the Crud functions for each aggregate.

Domain Objects

A domain object can be used as an aggregate by adding the event.AggregateRoot struct and finish implementing event.Aggregate. The AggregateRoot implements logic for adding events after they are either Raised by a command or Appended by the eventstore Load or service ApplyFn methods. It also tracks the uncommitted events that are saved using the eventstore Save method.

type User struct {
  Identity string ```json:"identity"`

  CreatedAt time.Time

  event.AggregateRoot
}

// StreamID for the aggregate when stored or loaded from ES.
func (a *User) StreamID() string {
	return "user-" + a.Identity
}
// ApplyEvent to the aggregate state.
func (a *User) ApplyEvent(lis ...event.Event) {
	for _, e := range lis {
		switch e := e.(type) {
		case *UserCreated:
			a.Identity = e.Identity
			a.CreatedAt = e.EventMeta().CreatedDate
        /* ... */
		}
	}
}
Events

Events are applied to the aggregate. They are defined by adding the event.Meta and implementing the getter/setters for event.Event

type UserCreated struct {
	eventMeta event.Meta

	Identity string
}

func (c *UserCreated) EventMeta() (m event.Meta) {
	if c != nil {
		m = c.eventMeta
	}
	return m
}
func (c *UserCreated) SetEventMeta(m event.Meta) {
	if c != nil {
		c.eventMeta = m
	}
}
Reading Events from EventStore

With a domain object that implements the event.Aggregate the event store client can load events and apply them using the Load(ctx, agg) method.

// GetUser populates an user from event store.
func (rw *User) GetUser(ctx context.Context, userID string) (*domain.User, error) {
	user := &domain.User{Identity: userID}

	err := rw.es.Load(ctx, user)
	if err != nil {
		if err != nil {
			if errors.Is(err, eventstore.ErrStreamNotFound) {
				return user, ErrNotFound
			}
			return user, err
		}
		return nil, err
	}
	return user, err
}
OnX Commands

An OnX command will validate the state of the domain object can have the command performed on it. If it can be applied it raises the event using event.Raise() Otherwise it returns an error.

// OnCreate raises an UserCreated event to create the user.
// Note: The handler will check that the user does not already exsist.
func (a *User) OnCreate(identity string) error {
    event.Raise(a, &UserCreated{Identity: identity})
    return nil
}

// OnScored will attempt to score a task.
// If the task is not in a Created state it will fail.
func (a *Task) OnScored(taskID string, score int64, attributes Attributes) error {
	if a.State != TaskStateCreated {
		return fmt.Errorf("task expected created, got %s", a.State)
	}
	event.Raise(a, &TaskScored{TaskID: taskID, Attributes: attributes, Score: score})
	return nil
}
Crud Operations for OnX Commands

The following functions in the aggregate service can be used to perform creation and updating of aggregates. The Update function will ensure the aggregate exists, where the Create is intended for non-existent aggregates. These can probably be combined into one function.

// Create is used when the stream does not yet exist.
func (rw *User) Create(
  ctx context.Context,
  identity string,
  fn func(*domain.User) error,
) (*domain.User, error) {
	session, err := rw.GetUser(ctx, identity)
	if err != nil && !errors.Is(err, ErrNotFound) {
		return nil, err
	}

	if err = fn(session); err != nil {
		return nil, err
	}

	_, err = rw.es.Save(ctx, session)

	return session, err
}

// Update is used when the stream already exists.
func (rw *User) Update(
  ctx context.Context,
  identity string,
  fn func(*domain.User) error,
) (*domain.User, error) {
	session, err := rw.GetUser(ctx, identity)
	if err != nil {
		return nil, err
	}

	if err = fn(session); err != nil {
		return nil, err
	}

	_, err = rw.es.Save(ctx, session)
	return session, err
}

⤋ Read More
In-reply-to » Hi, I am playing with making an event sourcing database. Its super alpha but I thought I would share since others are talking about databases and such.

Progress! so i have moved into working on aggregates. Which are a grouping of events that replayed on an object set the current state of the object. I came up with this little bit of generic wonder.

type PA[T any] interface {
	event.Aggregate
	*T
}

// Create uses fn to create a new aggregate and store in db.
func Create[A any, T PA[A]](ctx context.Context, es *EventStore, streamID string, fn func(context.Context, T) error) (agg T, err error) {
	ctx, span := logz.Span(ctx)
	defer span.End()

	agg = new(A)
	agg.SetStreamID(streamID)

	if err = es.Load(ctx, agg); err != nil {
		return
	}

	if err = event.NotExists(agg); err != nil {
		return
	}

	if err = fn(ctx, agg); err != nil {
		return
	}

	var i uint64
	if i, err = es.Save(ctx, agg); err != nil {
		return
	}

	span.AddEvent(fmt.Sprint("wrote events = ", i))

	return
}

fig. 1

This lets me do something like this:

a, err := es.Create(ctx, r.es, streamID, func(ctx context.Context, agg *domain.SaltyUser) error {
		return agg.OnUserRegister(nick, key)
})

fig. 2

I can tell the function the type being modified and returned using the function argument that is passed in. pretty cray cray.

⤋ Read More
In-reply-to » Hi, I am playing with making an event sourcing database. Its super alpha but I thought I would share since others are talking about databases and such.

Progress! so i have moved into working on aggregates. Which are a grouping of events that replayed on an object set the current state of the object. I came up with this little bit of generic wonder.

type PA[T any] interface {
	event.Aggregate
	*T
}

// Create uses fn to create a new aggregate and store in db.
func Create[A any, T PA[A]](ctx context.Context, es *EventStore, streamID string, fn func(context.Context, T) error) (agg T, err error) {
	ctx, span := logz.Span(ctx)
	defer span.End()

	agg = new(A)
	agg.SetStreamID(streamID)

	if err = es.Load(ctx, agg); err != nil {
		return
	}

	if err = event.NotExists(agg); err != nil {
		return
	}

	if err = fn(ctx, agg); err != nil {
		return
	}

	var i uint64
	if i, err = es.Save(ctx, agg); err != nil {
		return
	}

	span.AddEvent(fmt.Sprint("wrote events = ", i))

	return
}

fig. 1

This lets me do something like this:

a, err := es.Create(ctx, r.es, streamID, func(ctx context.Context, agg *domain.SaltyUser) error {
		return agg.OnUserRegister(nick, key)
})

fig. 2

I can tell the function the type being modified and returned using the function argument that is passed in. pretty cray cray.

⤋ Read More
In-reply-to » I did a take home software engineering test for a company recently, unfortunately I was really sick (have finally recovered) at the time 😢 I was also at the same time interviewing for an SRE position (as well as Software Engineering).

@prologic@twtxt.net Error handling especially in Go is very tricky I think. Even though the idea is simple, it’s fairly hard to actually implement and use in a meaningful way in my opinion. All this error wrapping or the lack of it and checking whether some specific error occurred is a mess. errors.As(…) just doesn’t feel natural. errors.Is(…) only just. I mainly avoided it. Yesterday evening I actually researched a bit about that and found this article on errors with Go 1.13. It shed a little bit of light, but I still have a long way to go, I reckon.

We tried several things but haven’t found the holy grail. Currently, we have a mix of different styles, but nothing feels really right. And having plenty of different approaches also doesn’t help, that’s right. I agree, error messages often end up getting wrapped way too much with useless information. We haven’t found a solution yet. We just noticed that it kind of depends on the exact circumstances, sometimes the caller should add more information, sometimes it’s better if the callee already includes what it was supposed to do.

To experiment and get a feel for yesterday’s research results I tried myself on the combined log parser and how to signal three different errors. I’m not happy with it. Any feedback is highly appreciated. The idea is to let the caller check (not implemented yet) whether a specific error occurred. That means I have to define some dedicated errors upfront (ErrInvalidFormat, ErrInvalidStatusCode, ErrInvalidSentBytes) that can be used in the err == ErrInvalidFormat or probably more correct errors.Is(err, ErrInvalidFormat) check at the caller.

All three errors define separate error categories and are created using errors.New(…). But for the invalid status code and invalid sent bytes cases I want to include more detail, the actual invalid number that is. Since these errors are already predefined, I cannot add this dynamic information to them. So I would need to wrap them à la fmt.Errorf("invalid sent bytes '%s': %w", sentBytes, ErrInvalidSentBytes"). Yet, the ErrInvalidSentBytes is wrapped and can be asserted later on using errors.Is(err, ErrInvalidSentBytes), but the big problem is that the message is repeated. I don’t want that!

Having a Python and Java background, exception hierarchies are a well understood concept I’m trying to use here. While typing this long message it occurs to me that this is probably the issue here. Anyways, I thought, I just create a ParseError type, that can hold a custom message and some causing error (one of the three ErrInvalid* above). The custom message is then returned at Error() and the wrapped cause will be matched in Is(…). I then just return a ParseError{fmt.Sprintf("invalid sent bytes '%s'", sentBytes), ErrInvalidSentBytes}, but that looks super weird.

I probably need to scrap the “parent error” ParseError and make all three “suberrors” three dedicated error types implementing Error() string methods where I create a useful error messages. Then the caller probably could just errors.Is(err, InvalidSentBytesError{}). But creating an instance of the InvalidSentBytesError type only to check for such an error category just does feel wrong to me. However, it might be the way to do this. I don’t know. To be tried. Opinions, anyone? Implementing a whole new type is some effort, that I want to avoid.

Alternatively just one ParseError containing an error kind enumeration for InvalidFormat and friends could be used. Also seen that pattern before. But that would then require the much more verbose var parseError ParseError; if errors.As(err, &parseError) && parseError.Kind == InvalidSentBytes { … } or something like that. Far from elegant in my eyes.

⤋ Read More

Hi, I am playing with making an event sourcing database. Its super alpha but I thought I would share since others are talking about databases and such.

It’s super basic. Using tidwall/wal as the disk backing. The first use case I am playing with is an implementation of msgbus. I can post events to it and read them back in reverse order.

I plan to expand it to handle other event sourcing type things like aggregates and projections.

Find it here: sour-is/ev

@prologic@twtxt.net @movq@www.uninformativ.de @lyse@lyse.isobeef.org

⤋ Read More

Hi, I am playing with making an event sourcing database. Its super alpha but I thought I would share since others are talking about databases and such.

It’s super basic. Using tidwall/wal as the disk backing. The first use case I am playing with is an implementation of msgbus. I can post events to it and read them back in reverse order.

I plan to expand it to handle other event sourcing type things like aggregates and projections.

Find it here: sour-is/ev

@prologic@twtxt.net @movq@www.uninformativ.de @lyse@lyse.isobeef.org

⤋ Read More

The Chromium super (inline cache) type confusion
In this post I’ll exploit CVE-2022-1134, a type confusion in Chrome that I reported in March 2022, which allows remote code execution (RCE) in the renderer sandbox of Chrome by a single visit to a malicious site. I’ll also look at some past vulnerabilities of this type and some implementation details of inline cache in V8, the JavaScript engine of Chrome. ⌘ Read more

⤋ Read More

Masks, Covid-19 test kits, plastic bottles: more than 9,000kg of litter found on Hong Kong’s hiking trails, seashore last year, green group says
Plastic bottles, bottle lids and buoys top three types of rubbish commonly found on seashore, while cigarettes, tissue paper and food packaging dot hiking trails, green group Ecobus says. ⌘ Read more

⤋ Read More

Gajim: Gajim 1.4.4
Gajim 1.4.4 comes with many improvements: emoji auto-complete, automatic theme switching when your desktop switches from light to dark in the evening, a completely reworked Gajim remote interface, and many bug fixes.

What’s New

After many emoji improvements in Gajim 1.4.3, this version comes with an emoji auto-complete while writing messages! As soon as you start typing a :, a popover will show you available emoji shortcodes, just like on Slack or Github 🎉

![Emoji auto-complete](ht … ⌘ Read more

⤋ Read More

Q: Are passphrases really more secure than cryptographically random passwords? 🤔

I have to wonder… It should be possible to do “passphrase” attacks just like “dictionary” attacks? How is a “phrase” any different to the character set you can type? Sure there are more possible “words” (at least) in the English language, but I’m not convinced.

⤋ Read More

Nix 2.7.0 released
We’re pleased to announce the availability of Nix 2.7.0. It will be
available from
NixOS - Getting Nix / NixOS.

Here are the release notes:

  • Nix will now make some helpful suggestions when you mistype something
    on the command line. For instance, if you type nix build
    nixpkgs#thunderbrd, it will suggest
    thunderbird.

  • A number of “default” flake output attributes have been renamed.
    These are:

    • defaultPackage.<system></system>packag ... ⌘ [Read more](https://nixos.org/blog/announcements.html#nix-2.7.0)

⤋ Read More

there are two types of panpsychism, and they can be distinguished by asking “if i shatter this glass, will it feel pain?”. one answers “yes”, the other answers “no clue, but probably not”

⤋ Read More

PEP 682: Format Specifier for Signed Zero
Though float and Decimal types can represent signed zero, in many
fields of mathematics negative zero is surprising or unwanted – especially
in the context of displaying an (often rounded) numerical result. This PEP
proposes an extension to the string format specification allowing negative
zero to be normalized to positive zero. ⌘ Read more

⤋ Read More

从 CPU 角度理解 Go 中的结构体内存对齐
大家好,我是 Go 学堂的渔夫子。今天跟大家聊聊结构体字段内存对齐相关的知识点。

原文链接: https://mp.weixin.qq.com/s/H3399AYE1MjaDRSllhaPrw

大家在写 Go 时有没有注意过,一个 struct 所占的空间不见得等于各个字段加起来的空间之和,甚至有时候把字段的顺序调整一下,struct 的所占空间又有不同的结果。

本文就从 cpu 读取内存的角度来谈谈内存对齐的原理。

01 结构体字段对齐示例

我们先从一个示例开始。T1 结构体,共有 3 个字段,类型分别为 int8,int64,int32。所以变量 t1 所属的类型占用的空间应该是 1+8+4=13 字节。但运行程序后,实际上是 24 字节。和我们计算的 13 字节不一样啊。如果我们把该结构体的字段调整成 T2 那样,结果是 16 字节。但和 13 字节还是不一样。这是为什么呢?

”`
type T1 struct {

f1 int8  // 1 byte
f2 int64 // ... ⌘ [Read more](https://gocn.vip/topics/20967)```

⤋ Read More

Eagle-eyed folks may have noticed that I made a few wee changes to this website.

The biggest, and most noticeable one is that I’ve filtered out all of the like type posts from appearing on the main feed, from rss, and in the archives. There were waaaaay to many, so filtering them out makes the entire website a lot more navigable. They are not gone, though, and I’ll probably keep sharing like type posts, but now they ar … ⌘ Read more

⤋ Read More

I just went to type the phrase “I avoid Linux like the plague” but then remembered that we’ve all learned that most people won’t actually go much out of their way to avoid the plague.

⤋ Read More

平凯星辰获评《金融电子化》2021 金融业新技术应用创新突出贡献奖
近日,《金融电子化》杂志社发布了 “2021 第十二届金融科技应用创新奖” 评选结果,平凯星辰(北京)科技有限公司(以下简称 “平凯星辰”)榜上有名,这是对平凯星辰在金融科技领域的创新成果与能力的充分肯定。

![在这里插入图片描述](https://img-blog.csdnimg.cn/6f798223462548d68361a7fd75b1ae3e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow … ⌘ Read more

⤋ Read More

Go 中这么多创建 error 的方式,你真的了解它们各自的应用场景吗
大家好,我是渔夫子。今天从应用场景的角度来聊聊我对 error 的理解。

原文链接: https://mp.weixin.qq.com/s/ncEemMJ0kQayVPJrnF9aew

01 什么是 Error

在 Go 中,error 是一种内建的数据类型。在 Go 中被定义为一个接口,定义如下:


<span class="c">// The error built-in interface type is the conventional interface for< ... ⌘ [Read more](https://gocn.vip/topics/20911)

⤋ Read More

Day 16 of Advent of Code is so confusing that I will not finish today’s puzzle. I wonder if yesterday was my last day with Advent of Code, or will the puzzles become more understandable and easier again in the next few days? Maybe I’m just more the practical type. I like programming, but such complex algorithms are not really my thing. And in the end, Advent of Code is supposed to be fun… ⌘ Read more

⤋ Read More

go 1.18 泛型初体验

go 1.18 泛型初体验

go.1.18beta 版发布,众所周知这个 go.1.18 版本会默认启用 go 泛型。这个版本也号称 go 最大的版本改动。

初识 golang 的泛型

我们写一个 demo 来看看 go 的泛型是长啥样

”`

package main

import (

<span class="s">"fmt"</span>

)

type OrderTypesRead more”`

⤋ Read More

PEP 677: Callable Type Syntax
This PEP introduces a concise and friendly syntax for callable types,
supporting the same functionality as typing.Callable but with an
arrow syntax inspired by the syntax for typed function
signatures. This allows types like Callable[[int, str], bool] to
be written (int, str) -> bool. ⌘ Read more

⤋ Read More

** Operators in C **
Following up my notes on Data Types and Variables in C here are notes on operators in C.

An operator is a symbol that represents a mathematical or logical operation. An operator effects operands.

C provides a number of operators.

Some arithmetic operators include,

”`hljs plaintext

+

*
/
%
”`

% is the most exciting of the list, it is called modulo and it returns the remainder after division. Of note, modulo c … ⌘ Read more

⤋ Read More

My nutritional supplements aim should be:

  • 1 or 1.5 cups of lentils (or any beans you might like better).
  • 2 or 2.5 cups of bitter greens.
  • 1 cup of your favourite protein (or an egg), grilled, or fried with a little of olive oil.
  • 1 or 2 tomatoes, or a handful if of the cherry type.
  • No added sugars. If it is sweet, make it have fibre.
  • No added salt (or very little and ionised), as salt is everywhere.

Related, I tried wild rice for the first time yesterday. It was different, in a good way.

⤋ Read More

** Data Types and Variables in C **
I’ve been writing a heap of Lua lately — this has lead to my becoming interested, again, in C. Here are some ancient notes I dug up on the most basics of data types and variables in C.

All of a computer’s memory is comprised of bits. A sequence of 8 bits forms a byte. A group of bytes (typically 4 or 8) form a word. Each word is associated with a memory address. The address increases by 1 with each byte of memory.

In C, a byte is an object that is as big as t … ⌘ Read more

⤋ Read More

Looking for a Docker Alternative? Consider This.
Docker recently announced updates and extensions to our product subscriptions. Docker CEO Scott Johnston also posted a blog about the changes. Much of the discussion centered on what the licensing changes mean for users of Docker Desktop, which remains free for small businesses and several other user types, but now requires a paid subscription — starting […]

The post [Looking for a Docker Alternative? Consider This.](https://www.doc … ⌘ Read more

⤋ Read More

Gajim: Development News September 2021
September brought many updates under the hood. With big changes coming up in Gajim 1.4, many parts of the code have to be touched. These changes remain mostly invisible for users, but make Gajim more robust. In some cases, this results in visible improvements as well: Both Add Contact and Start Chat windows are now detecting the type of chat behind an address.

Changes in Gajim

Since development on Gajim 1.4 started, a lot has changed under the hood. … ⌘ Read more

⤋ Read More

There is a type of flourishing available to a cat that is only rarely available to us. A human is never simply what she is, but is always striving to become something she is, as yet, not. This is the result of a self-image – a conception of herself and what her life should be – which, when unrealised, can occasion frustration and despair. What the Cat Knows (2020) | Hacker News

⤋ Read More

The advice about interviewing being exhausting is spot-on. Recruiting, interviewing, and hiring candidates is a very different type of work than most of us engineering types enjoy. Until you’ve developed a thick skin, it can also be emotionally draining to reject candidate after candidate. I’m an interviewer at my company and burnt out | Hacker News

⤋ Read More