Class: Oxygene::CARRepo

Inherits:
CARArchive show all
Defined in:
lib/oxygene/car_repo.rb

Overview

A CAR archive containing an account's ATProto repository, as returned by the com.atproto.sync.getRepo PDS endpoint.

Oxygene currently supports repositories in the v3 format version.

Related specifications:

Instance Attribute Summary collapse

Attributes inherited from CARArchive

#roots

Instance Method Summary collapse

Methods inherited from CARArchive

convert_data, #inspect, make_bytes, make_cid_link, #parsed_sections, #section_with_cid, #sections

Constructor Details

#initialize(data) ⇒ CARRepo

Creates an ATProto repository reader from an in-memory CAR file.

Parameters:

  • data (String)

    CAR archive file as a binary string

Raises:

  • (DecodeError)

    if the CAR header has missing or invalid fields, the root commit is missing, or a section has invalid data

  • (UnsupportedError)

    if the archive, section CID or repository is in an unsupported version



30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/oxygene/car_repo.rb', line 30

def initialize(data)
  super

  raise DecodeError, "CAR repository has no root commit" if roots.empty?

  @commit_section = section_with_cid(roots.first, use_map: true, return_body: false)
  raise DecodeError, "Root commit not found in the archive: #{roots.first.inspect}" if @commit_section.nil?

  commit_body = @commit_section.decoded_body
  raise DecodeError, "Commit object should be a hash" unless commit_body.is_a?(Hash)

  repo_version = commit_body['version']
  raise UnsupportedError, "Unexpected repository version: #{repo_version.inspect}" unless repo_version == 3
end

Instance Attribute Details

#commit_sectionCARSection (readonly)

Returns the CAR archive section containing the repository's root commit.

Returns:

  • (CARSection)

    section identified by the first CAR root CID



22
23
24
# File 'lib/oxygene/car_repo.rb', line 22

def commit_section
  @commit_section
end

Instance Method Details

#commitHash

Returns the repository commit data.

The commit is decoded from the body of the #commit_section. The data is returned in the ATProto JSON representation – CID links and binary strings are represented using $link and $bytes objects respectively. Use #commit_section to access the original CBOR bytes or decoded CBOR values without conversion.

See the ATProto repository spec for what fields the commit object is expected to contain.

Returns:

  • (Hash)

    root commit data



58
59
60
# File 'lib/oxygene/car_repo.rb', line 58

def commit
  commit_section.json_body
end

#walk_all_nodes(starting_node_cid = nil) {|key, cid| ... } ⇒ Object

Walks through the repository MST tree, running the passed block for each record in the repo in alphabetical key order.

Records are stored in the Merkle Search Tree structure in nodes with assigned keys, where each key is a record path (NSID collection + rkey, e.g. app.bsky.feed.post/3juhznhw65225). The iterator returns all records one by one sorted alphabetically, so the list is sorted by collection first and then by rkey within a collection. The callback is passed the path key and the Oxygene::CID of the section which contains the actual record data, The CID can be used to extract the data through a call to Oxygene::CARArchive#section_with_cid (you almost certainly want to pass use_map: true).

Normally you can skip the starting_node_cid parameter, in which case the tree traversal begins at the repository root referenced by the CAR header and covers the entire tree. If a starting CID is passed, the traversal will start from a given node covering only a subtree.

Parameters:

  • starting_node_cid (CID, String, nil) (defaults to: nil)

    CID of the tree node to start from, or nil to start at the repository root

Yields:

  • (key, cid)

    path key of a given record and the CID of its value block

Yield Parameters:

  • key (String)

    record path key, i.e. collection + / + rkey

  • cid (CID)

    content identifier of the section containing the record value

Raises:

  • (DecodeError)

    if a requested section is missing, a section is truncated or malformed, or a CID is invalid

  • (UnsupportedError)

    if a section uses an unsupported CID encoding



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/oxygene/car_repo.rb', line 87

def walk_all_nodes(starting_node_cid = nil, &block)
  if starting_node_cid.nil?
    commit = commit_section.decoded_body
    tree_top_cid = commit['data'].value
    return walk_all_nodes(tree_top_cid, &block)
  end

  data = section_with_cid(starting_node_cid, use_map: true, return_body: false)&.decoded_body
  raise DecodeError, "MST node not found in the archive: #{starting_node_cid.inspect}" if data.nil?

  if data['l']
    walk_all_nodes(data['l'].value, &block)
  end

  previous = nil

  (data['e'] || []).each do |e|
    if previous
      key = previous[0...e['p']]
      key << e['k']
    else
      key = e['k']
    end

    previous = key

    block.call(key, CID.from_cbor_tag(e['v']))

    if e['t']
      walk_all_nodes(e['t'].value, &block)
    end
  end
end