S8: a direct message joins a conversation only when its participants are exactly that conversation's members, found through a new DmGroup.ParticipantsKey (a hash of the sorted members). A remote context no longer decides anything: it let anyone who knew a conversation's context post into it, and joining by context while dropping a participant would have shown a reply to someone it was not addressed to. A context is kept only when it is on the author's origin. Sending a DM to the same people again reuses their conversation instead of opening a new one. S9: Group.Kind is Circle or Community. A circle is not a federated actor: its actor, collections, WebFinger and inbox answer 404, a remote Follow is refused, and posts in it are IsLocalOnly - never delivered, never in an outbox, never served. Communities keep today's behaviour until P4. Migration _003 makes every existing group a circle, marks their posts local-only and backfills the conversation keys. End-to-end inbox tests sign real deliveries from a fake peer: a context injection, a forged activity id, a note attributed to someone else, a cross-origin object, a bad signature, junk bodies and a Follow of a circle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
32 lines
1.4 KiB
C#
32 lines
1.4 KiB
C#
using PrivaPub.ClientModels.Resources;
|
|
using PrivaPub.ClientModels.ValidatorAttributes;
|
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
|
namespace PrivaPub.ClientModels.Group
|
|
{
|
|
public class InsertGroupForm
|
|
{
|
|
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource))]
|
|
public string AvatarId { get; set; }
|
|
|
|
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)),
|
|
StringLength(32, MinimumLength = 3, ErrorMessageResourceName = "StringLengthMinMax", ErrorMessageResourceType = typeof(ErrorsResource)),
|
|
RegularExpression("^[a-z0-9_]+$", ErrorMessageResourceName = "EmptySpacesNotAllowed", ErrorMessageResourceType = typeof(ErrorsResource)),
|
|
NoWhiteSpaces]
|
|
public string UserName { get; set; }
|
|
|
|
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)),
|
|
StringLength(64, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))]
|
|
public string Name { get; set; }
|
|
|
|
[StringLength(2000, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))]
|
|
public string Description { get; set; }
|
|
|
|
public string InvitationPassword { get; set; }
|
|
public bool IsCommunity { get; set; }
|
|
public bool IsDiscoverable { get; set; } = true;
|
|
public bool ManuallyApprovesMembers { get; set; }
|
|
}
|
|
}
|