This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

appview: stop ingesting spindle server / member records

Signed-off-by: Seongmin Lee <git@boltless.me>

author
Seongmin Lee
date (Jul 29, 2026, 9:01 PM +0900) commit c59b89c3 parent d724c161 change-id msuqusww
-355
-311
appview/ingester.go
··· 97 97 err = i.ingestArtifact(ctx, e, l) 98 98 case tangled.ActorProfileNSID: 99 99 err = i.ingestProfile(ctx, e, l) 100 - case tangled.SpindleMemberNSID: 101 - err = i.ingestSpindleMember(ctx, e, l) 102 - case tangled.SpindleNSID: 103 - err = i.ingestSpindle(ctx, e, l) 104 100 case tangled.KnotMemberNSID: 105 101 err = i.ingestKnotMember(ctx, e, l) 106 102 case tangled.KnotNSID: ··· 664 660 return nil 665 661 } 666 662 667 - func (i *Ingester) ingestSpindleMember(ctx context.Context, e *jmodels.Event, l *slog.Logger) error { 668 - did := e.Did 669 - var err error 670 - 671 - l = l.With("handler", "ingestSpindleMember") 672 - 673 - switch e.Commit.Operation { 674 - case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate: 675 - raw := json.RawMessage(e.Commit.Record) 676 - record := tangled.SpindleMember{} 677 - err = json.Unmarshal(raw, &record) 678 - if err != nil { 679 - l.Error("invalid record", "err", err) 680 - return err 681 - } 682 - 683 - // only spindle owner can invite to spindles 684 - ok, err := i.Enforcer.IsSpindleInviteAllowed(did, record.Instance) 685 - if err != nil { 686 - return fmt.Errorf("failed to check invite permission: %w", err) 687 - } 688 - if !ok { 689 - if verifyErr := i.verifySpindle(ctx, record.Instance, did); verifyErr != nil { 690 - return fmt.Errorf("invite denied and verify failed: %w", verifyErr) 691 - } 692 - ok, err = i.Enforcer.IsSpindleInviteAllowed(did, record.Instance) 693 - if err != nil { 694 - return fmt.Errorf("failed to re-check invite permission: %w", err) 695 - } 696 - if !ok { 697 - return fmt.Errorf("invite denied for did %s on spindle %s", did, record.Instance) 698 - } 699 - } 700 - 701 - memberId, err := i.IdResolver.ResolveIdent(ctx, record.Subject) 702 - if err != nil { 703 - return err 704 - } 705 - 706 - if memberId.Handle.IsInvalidHandle() { 707 - return fmt.Errorf("invalid handle for member %s", record.Subject) 708 - } 709 - 710 - existing, err := db.GetSpindleMembers(i.Db, 711 - orm.FilterEq("did", did), 712 - orm.FilterEq("rkey", e.Commit.RKey), 713 - ) 714 - if err != nil { 715 - return fmt.Errorf("failed to look up existing member: %w", err) 716 - } 717 - if len(existing) > 1 { 718 - return fmt.Errorf("multiple spindle members with rkey %s", e.Commit.RKey) 719 - } 720 - 721 - tx, err := i.Db.Begin() 722 - if err != nil { 723 - return fmt.Errorf("failed to start txn: %w", err) 724 - } 725 - committed := false 726 - defer func() { 727 - if committed { 728 - return 729 - } 730 - tx.Rollback() 731 - i.Enforcer.E.LoadPolicy() 732 - }() 733 - 734 - if len(existing) == 1 { 735 - prev := existing[0] 736 - if prev.Instance != record.Instance || prev.Subject != memberId.DID { 737 - if err = db.RemoveSpindleMember(tx, 738 - orm.FilterEq("did", did), 739 - orm.FilterEq("rkey", e.Commit.RKey), 740 - ); err != nil { 741 - return fmt.Errorf("failed to remove stale row: %w", err) 742 - } 743 - if err = i.Enforcer.RemoveSpindleMember(prev.Instance, prev.Subject.String()); err != nil { 744 - return fmt.Errorf("failed to remove stale ACL: %w", err) 745 - } 746 - } 747 - } 748 - 749 - if err = db.AddSpindleMember(tx, models.SpindleMember{ 750 - Did: syntax.DID(did), 751 - Rkey: e.Commit.RKey, 752 - Instance: record.Instance, 753 - Subject: memberId.DID, 754 - }); err != nil { 755 - return fmt.Errorf("failed to add to db: %w", err) 756 - } 757 - 758 - if err = i.Enforcer.AddSpindleMember(record.Instance, memberId.DID.String()); err != nil { 759 - return fmt.Errorf("failed to update ACLs: %w", err) 760 - } 761 - 762 - if err = tx.Commit(); err != nil { 763 - return fmt.Errorf("failed to commit txn: %w", err) 764 - } 765 - 766 - if err = i.Enforcer.E.SavePolicy(); err != nil { 767 - return fmt.Errorf("failed to save ACLs: %w", err) 768 - } 769 - committed = true 770 - 771 - l.Info("upserted spindle member") 772 - case jmodels.CommitOperationDelete: 773 - rkey := e.Commit.RKey 774 - 775 - // get record from db first 776 - members, err := db.GetSpindleMembers( 777 - i.Db, 778 - orm.FilterEq("did", did), 779 - orm.FilterEq("rkey", rkey), 780 - ) 781 - if err != nil || len(members) != 1 { 782 - return fmt.Errorf("failed to get member: %w, len(members) = %d", err, len(members)) 783 - } 784 - member := members[0] 785 - 786 - tx, err := i.Db.Begin() 787 - if err != nil { 788 - return fmt.Errorf("failed to start txn: %w", err) 789 - } 790 - committed := false 791 - defer func() { 792 - if committed { 793 - return 794 - } 795 - tx.Rollback() 796 - i.Enforcer.E.LoadPolicy() 797 - }() 798 - 799 - // remove record by rkey && update enforcer 800 - if err = db.RemoveSpindleMember( 801 - tx, 802 - orm.FilterEq("did", did), 803 - orm.FilterEq("rkey", rkey), 804 - ); err != nil { 805 - return fmt.Errorf("failed to remove from db: %w", err) 806 - } 807 - 808 - // update enforcer 809 - err = i.Enforcer.RemoveSpindleMember(member.Instance, member.Subject.String()) 810 - if err != nil { 811 - return fmt.Errorf("failed to update ACLs: %w", err) 812 - } 813 - 814 - if err = tx.Commit(); err != nil { 815 - return fmt.Errorf("failed to commit txn: %w", err) 816 - } 817 - 818 - if err = i.Enforcer.E.SavePolicy(); err != nil { 819 - return fmt.Errorf("failed to save ACLs: %w", err) 820 - } 821 - committed = true 822 - 823 - l.Info("removed spindle member") 824 - } 825 - 826 - return nil 827 - } 828 - 829 - func (i *Ingester) ingestSpindle(ctx context.Context, e *jmodels.Event, l *slog.Logger) error { 830 - did := e.Did 831 - var err error 832 - 833 - l = l.With("handler", "ingestSpindle") 834 - 835 - switch e.Commit.Operation { 836 - case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate: 837 - raw := json.RawMessage(e.Commit.Record) 838 - record := tangled.Spindle{} 839 - err = json.Unmarshal(raw, &record) 840 - if err != nil { 841 - l.Error("invalid record", "err", err) 842 - return err 843 - } 844 - 845 - instance := e.Commit.RKey 846 - 847 - err := db.AddSpindle(i.Db, models.Spindle{ 848 - Owner: syntax.DID(did), 849 - Instance: instance, 850 - }) 851 - if err != nil { 852 - l.Error("failed to add spindle to db", "err", err, "instance", instance) 853 - return err 854 - } 855 - 856 - if err := i.verifySpindle(ctx, instance, did); err != nil { 857 - l.Warn("failed to verify spindle", "instance", instance, "did", did, "err", err) 858 - } 859 - 860 - l.Info("ingested record", "instance", instance) 861 - return nil 862 - 863 - case jmodels.CommitOperationDelete: 864 - instance := e.Commit.RKey 865 - 866 - // get record from db first 867 - spindles, err := db.GetSpindles( 868 - ctx, 869 - i.Db, 870 - orm.FilterEq("owner", did), 871 - orm.FilterEq("instance", instance), 872 - ) 873 - if err != nil || len(spindles) != 1 { 874 - return fmt.Errorf("failed to get spindles: %w, len(spindles) = %d", err, len(spindles)) 875 - } 876 - spindle := spindles[0] 877 - 878 - tx, err := i.Db.Begin() 879 - if err != nil { 880 - return fmt.Errorf("failed to start txn: %w", err) 881 - } 882 - defer func() { 883 - tx.Rollback() 884 - i.Enforcer.E.LoadPolicy() 885 - }() 886 - 887 - // remove spindle members first 888 - err = db.RemoveSpindleMember( 889 - tx, 890 - orm.FilterEq("owner", did), 891 - orm.FilterEq("instance", instance), 892 - ) 893 - if err != nil { 894 - return fmt.Errorf("failed to remove spindle members: %w", err) 895 - } 896 - 897 - err = db.DeleteSpindle( 898 - tx, 899 - orm.FilterEq("owner", did), 900 - orm.FilterEq("instance", instance), 901 - ) 902 - if err != nil { 903 - return fmt.Errorf("failed to delete spindle: %w", err) 904 - } 905 - 906 - if spindle.Verified != nil { 907 - err = i.Enforcer.RemoveSpindle(instance) 908 - if err != nil { 909 - return fmt.Errorf("failed to remove spindle from enforcer: %w", err) 910 - } 911 - } 912 - 913 - err = tx.Commit() 914 - if err != nil { 915 - return fmt.Errorf("failed to commit txn: %w", err) 916 - } 917 - 918 - err = i.Enforcer.E.SavePolicy() 919 - if err != nil { 920 - return fmt.Errorf("failed to save ACLs: %w", err) 921 - } 922 - 923 - l.Info("ingested record", "instance", instance) 924 - } 925 - 926 - return nil 927 - } 928 - 929 663 func (i *Ingester) ingestString(e *jmodels.Event, l *slog.Logger) error { 930 664 did := e.Did 931 665 rkey := e.Commit.RKey ··· 1281 1015 return serververify.MarkKnotVerified(i.Db, i.Enforcer, domain, did) 1282 1016 } 1283 1017 1284 - func (i *Ingester) verifySpindle(ctx context.Context, instance, did string) error { 1285 - spindles, err := db.GetSpindles(ctx, i.Db, 1286 - orm.FilterEq("instance", instance), 1287 - orm.FilterEq("owner", did), 1288 - ) 1289 - if err != nil { 1290 - return fmt.Errorf("look up spindle: %w", err) 1291 - } 1292 - if len(spindles) != 1 { 1293 - return fmt.Errorf("no spindle for %s by %s", instance, did) 1294 - } 1295 - if spindles[0].Verified != nil { 1296 - return nil 1297 - } 1298 - 1299 - err = retry.Do( 1300 - func() error { return serververify.RunVerification(ctx, instance, did, i.Config.Core.Dev) }, 1301 - retry.Context(ctx), 1302 - retry.Attempts(verifyAttempts), 1303 - retry.Delay(verifyMinDelay), 1304 - retry.MaxDelay(verifyMaxDelay), 1305 - retry.DelayType(retry.BackOffDelay), 1306 - retry.LastErrorOnly(true), 1307 - ) 1308 - if err != nil { 1309 - return fmt.Errorf("verify: %w", err) 1310 - } 1311 - _, err = serververify.MarkSpindleVerified(i.Db, i.Enforcer, instance, did) 1312 - return err 1313 - } 1314 - 1315 1018 const sweepConcurrency = 4 1316 1019 1317 1020 func (i *Ingester) SweepPendingVerifications() { ··· 1334 1037 } 1335 1038 } 1336 1039 1337 - spindles, err := db.GetSpindles(i.Ctx, i.Db, orm.FilterIs("verified", nil)) 1338 - if err != nil { 1339 - l.Error("failed to list unverified spindles", "err", err) 1340 - g.Wait() 1341 - return 1342 - } 1343 - for _, s := range spindles { 1344 - g.Go(func() error { 1345 - if err := i.verifySpindle(i.Ctx, s.Instance, s.Owner.String()); err != nil { 1346 - l.Warn("verify spindle failed", "instance", s.Instance, "owner", s.Owner, "err", err) 1347 - } 1348 - return nil 1349 - }) 1350 - } 1351 1040 g.Wait() 1352 1041 } 1353 1042
-44
appview/oauth/handler.go
··· 25 25 "tangled.org/core/appview/models" 26 26 "tangled.org/core/consts" 27 27 "tangled.org/core/idresolver" 28 - "tangled.org/core/orm" 29 28 "tangled.org/core/tid" 30 29 ) 31 30 ··· 119 118 o.ensureProfileRecord(sessData.AccountDID, sessData.SessionID) 120 119 121 120 go o.addToDefaultKnot(sessData.AccountDID) 122 - go o.addToDefaultSpindle(sessData.AccountDID.String()) 123 121 go o.autoClaimTnglShDomain(sessData.AccountDID.String()) 124 122 125 123 if !o.Config.Core.Dev { ··· 165 163 return errors.As(err, &xrpcErr) && 166 164 errors.As(xrpcErr.Wrapped, &xrpcBody) && 167 165 xrpcBody.ErrStr == "RepoDeactivated" 168 - } 169 - 170 - func (o *OAuth) addToDefaultSpindle(did string) { 171 - l := o.Logger.With("subject", did) 172 - 173 - // use the tangled.sh app password to get an accessJwt 174 - // and create an sh.tangled.spindle.member record with that 175 - spindleMembers, err := db.GetSpindleMembers( 176 - o.Db, 177 - orm.FilterEq("instance", "spindle.tangled.sh"), 178 - orm.FilterEq("subject", did), 179 - ) 180 - if err != nil { 181 - l.Error("failed to get spindle members", "err", err) 182 - return 183 - } 184 - 185 - if len(spindleMembers) != 0 { 186 - l.Warn("already a member of the default spindle") 187 - return 188 - } 189 - 190 - l.Debug("adding to default spindle") 191 - session, err := o.getAppPasswordSession() 192 - if err != nil { 193 - l.Error("failed to create session", "err", err) 194 - return 195 - } 196 - 197 - record := tangled.SpindleMember{ 198 - LexiconTypeID: tangled.SpindleMemberNSID, 199 - Subject: did, 200 - Instance: consts.DefaultSpindle, 201 - CreatedAt: time.Now().Format(time.RFC3339), 202 - } 203 - 204 - if err := session.putRecord(record, tangled.SpindleMemberNSID); err != nil { 205 - l.Error("failed to add to default spindle", "err", err) 206 - return 207 - } 208 - 209 - l.Debug("successfully added to default spindle", "did", did) 210 166 } 211 167 212 168 type onboardAction int