{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://opengenealogyai.org/schemas/maxgen/v1/dna.schema.json",
  "title": "MaxDNA",
  "description": "MAXGEN v1.4 â€” Maxwell Genealogy Standard. DNA test metadata and match assertions linked to a MaxPerson. NEVER stores raw genotype data (FASTQ, VCF, raw SNP files). Kit identifiers are always hashed (HMAC-SHA-256 with system salt, NOT plain SHA-256) before storage. Always tier2-private; never redistributed. Requires explicit opt-in consent from the test subject (or legal guardian). Match records carry cm_map_version and phasing_status so downstream triangulation can reconcile across providers. Schema content stable since v1.2; version stamp follows lockstep MAXGEN release versioning.",
  "type": "object",
  "required": [
    "dna_id", "schema_version", "person_id", "test_type",
    "kit_id_hash", "kit_source", "is_living_flag",
    "redistribution_license", "raw_genotype_stored",
    "consent_status", "asserted_by", "asserted_at"
  ],
  "properties": {
    "dna_id": {
      "type": "string",
      "format": "uuid",
      "description": "Globally unique identifier for this DNA assertion."
    },
    "schema_version": {
      "type": "string",
      "const": "1.9",
      "description": "MAXGEN version this DNA record conforms to."
    },
    "person_id": {
      "type": "string",
      "format": "uuid",
      "description": "Foreign key to the MaxPerson this DNA test belongs to."
    },
    "test_type": {
      "type": "string",
      "enum": ["autosomal", "y_dna", "mt_dna", "x_dna", "whole_genome", "combined"],
      "description": "Type of DNA test performed."
    },
    "test_provider": {
      "type": "string",
      "enum": [
        "23andme", "ancestrydna", "ftdna", "myheritage",
        "livingdna", "tellmegen", "nebula", "dante_labs",
        "gedmatch_upload", "wikitree_linked", "opensnp", "yfull",
        "academic_research", "other"
      ],
      "description": "Company or platform that performed or hosts the original test."
    },
    "test_date": {
      "type": "object",
      "description": "Approximate date the test was taken. Year-only is fine.",
      "properties": {
        "year_min": { "type": "integer", "minimum": 1995, "maximum": 2050 },
        "year_max": { "type": "integer", "minimum": 1995, "maximum": 2050 },
        "month": { "type": "integer", "minimum": 1, "maximum": 12 }
      }
    },
    "kit_id_hash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "SHA-256 hash of the original kit number. NEVER store the raw kit ID."
    },
    "kit_source": {
      "type": "string",
      "description": "Where this kit metadata came from (e.g. 'wikitree_api', 'opensnp_public', 'user_self_report')."
    },
    "haplogroup_y": {
      "type": "string",
      "pattern": "^[A-Z][A-Za-z0-9\\-]*$",
      "description": "Y-DNA haplogroup designation (paternal line), e.g. 'R-M269', 'I-M253'. Only applicable for male test subjects."
    },
    "haplogroup_mt": {
      "type": "string",
      "pattern": "^[A-Z][A-Za-z0-9\\-]*$",
      "description": "mtDNA haplogroup designation (maternal line), e.g. 'H1a1', 'U5b1'."
    },
    "haplogroup_y_confidence": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0
    },
    "haplogroup_mt_confidence": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0
    },
    "ancestry_composition": {
      "type": "array",
      "description": "Ethnicity/admixture estimates from the test provider. Sums to ~100%.",
      "items": {
        "type": "object",
        "required": ["region", "percentage"],
        "properties": {
          "region": {
            "type": "string",
            "description": "Region label as reported by the provider (e.g. 'Ireland & Scotland', 'Ashkenazi Jewish', 'West African')."
          },
          "percentage": {
            "type": "number",
            "minimum": 0.0,
            "maximum": 100.0
          },
          "confidence": {
            "type": "number",
            "minimum": 0.0,
            "maximum": 1.0
          },
          "iso_country_codes": {
            "type": "array",
            "items": { "type": "string", "pattern": "^[A-Z]{2}$" },
            "description": "ISO 3166-1 alpha-2 country codes this region maps to."
          }
        }
      }
    },
    "endogamy_flag": {
      "type": "boolean",
      "default": false,
      "description": "True if subject belongs to an endogamous population (Ashkenazi, Acadian, Mennonite, etc.) where standard cM thresholds are unreliable."
    },
    "endogamy_population": {
      "type": "string",
      "description": "Specific endogamous population if endogamy_flag is true."
    },
    "matches": {
      "type": "array",
      "description": "DNA matches with other kits. Each match is an assertion; multiple assertions with different confidences are allowed.",
      "items": {
        "type": "object",
        "required": ["match_kit_hash", "shared_cm", "asserted_at"],
        "properties": {
          "match_kit_hash": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$",
            "description": "SHA-256 hash of the matching kit's ID."
          },
          "match_person_id": {
            "type": "string",
            "format": "uuid",
            "description": "Foreign key to MaxPerson IF the match's kit has been linked to a known person. May be null for anonymous matches."
          },
          "match_display_name": {
            "type": "string",
            "description": "Match's display name or handle as shown by the source service. May be a pseudonym."
          },
          "shared_cm": {
            "type": "number",
            "minimum": 0,
            "maximum": 7000,
            "description": "Total shared centimorgans across all segments. Max 7000 admits identical twins (~6800 cM); parent-child is ~3400."
          },
          "longest_segment_cm": {
            "type": "number",
            "minimum": 0,
            "maximum": 285,
            "description": "Length of the single longest shared segment in cM. Max 285 admits a full chromosome 1 (~281 cM)."
          },
          "cm_map_version": {
            "type": "string",
            "enum": ["hapmap", "decode", "aabb", "shapeit4", "unknown"],
            "default": "unknown",
            "description": "Which recombination map produced the cM values in this match. Different maps yield non-comparable cM totals; capture the source so downstream tools can reconcile."
          },
          "phasing_status": {
            "type": "string",
            "enum": ["maternal", "paternal", "both", "unphased", "unknown"],
            "default": "unknown",
            "description": "Which parental side the shared segments fall on, when known. Required for serious triangulation; 'unphased' means a phased call hasn't been attempted, 'unknown' means it has but was inconclusive."
          },
          "segment_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of distinct shared chromosomal segments."
          },
          "inferred_relationship": {
            "type": "string",
            "enum": [
              "identical_twin", "parent_child", "full_sibling",
              "half_sibling", "grandparent", "aunt_uncle",
              "first_cousin", "first_cousin_once_removed",
              "second_cousin", "second_cousin_once_removed",
              "third_cousin", "fourth_cousin", "fifth_or_more_distant",
              "endogamous_unclear", "unknown"
            ],
            "description": "Most likely relationship category given shared_cm."
          },
          "relationship_confidence": {
            "type": "number",
            "minimum": 0.0,
            "maximum": 1.0
          },
          "match_source": {
            "type": "string",
            "enum": ["23andme", "ancestrydna", "ftdna", "myheritage", "gedmatch", "wikitree_dna_connections", "user_uploaded", "other"],
            "description": "Where this match assertion came from."
          },
          "chromosome_segments": {
            "type": "array",
            "description": "Individual shared segments for triangulation analysis. May be empty if only totals are available.",
            "items": {
              "type": "object",
              "required": ["chromosome", "start_cm", "end_cm"],
              "properties": {
                "chromosome": {
                  "type": "string",
                  "enum": ["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","X","Y","MT"]
                },
                "start_position": {
                  "type": "integer",
                  "description": "Genomic start position (build 37 or 38 â€” see build_version)."
                },
                "end_position": {
                  "type": "integer"
                },
                "start_cm": {
                  "type": "number",
                  "description": "Genetic start position in centimorgans."
                },
                "end_cm": {
                  "type": "number"
                },
                "snp_count": {
                  "type": "integer",
                  "minimum": 0
                },
                "build_version": {
                  "type": "string",
                  "enum": ["GRCh37", "GRCh38"],
                  "default": "GRCh37"
                }
              }
            }
          },
          "triangulated_with": {
            "type": "array",
            "description": "Other match_kit_hash values that share the same segments. Foundation for ancestor triangulation.",
            "items": {
              "type": "string",
              "pattern": "^[a-f0-9]{64}$"
            }
          },
          "common_ancestor_candidates": {
            "type": "array",
            "description": "MaxPerson IDs of candidate most-recent common ancestors with per-candidate confidence. Real DNA matches often have multiple plausible MRCAs; do not force a single answer.",
            "items": {
              "type": "object",
              "required": ["person_id", "confidence"],
              "properties": {
                "person_id": { "type": "string", "format": "uuid" },
                "confidence": { "type": "number", "minimum": 0.0, "maximum": 1.0 },
                "inference_method": {
                  "type": "string",
                  "enum": ["manual", "tree_intersection", "triangulation", "shared_surname_geography", "other"]
                }
              }
            }
          },
          "asserted_by": {
            "type": "string",
            "description": "Agent or contributor ID that made this match assertion."
          },
          "asserted_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    },
    "external_ids": {
      "type": "object",
      "description": "Cross-service kit aliases. Keys are service identifiers, values are HASHED IDs only.",
      "additionalProperties": {
        "type": "string",
        "pattern": "^[a-f0-9]{64}$"
      }
    },
    "is_living_flag": {
      "type": "boolean",
      "description": "True if the DNA-test subject may be living. When true, all open endpoints return 404 and access requires authenticated owner."
    },
    "redistribution_license": {
      "type": "string",
      "const": "tier2-private",
      "description": "DNA data is ALWAYS tier2-private. No other value is permitted, regardless of contributor consent. Genetic data has cascading privacy implications for relatives who never consented."
    },
    "raw_genotype_stored": {
      "type": "boolean",
      "const": false,
      "description": "MAXGEN does not store raw genotype data (FASTQ, VCF, 23andMe raw export, etc.). This field must always be false. Raw data should remain with the test provider or with the user's own storage."
    },
    "external_raw_data_location": {
      "type": "string",
      "description": "Free-text note about where raw data is held (e.g. 'user's local drive', 'opensnp.org public', '23andme account'). NOT a URL or path we control."
    },
    "consent_status": {
      "type": "string",
      "enum": [
        "subject_explicit_opt_in",
        "guardian_consent",
        "public_dataset_redistribution_allowed",
        "deceased_pre_2000",
        "withdrawn",
        "pending"
      ],
      "description": "Legal basis for storing this DNA metadata. Anything other than the first four blocks all reads."
    },
    "consented_at": {
      "type": "string",
      "format": "date-time"
    },
    "consent_evidence_url": {
      "type": "string",
      "format": "uri",
      "description": "Link to signed consent record (e.g. signed PDF stored in tier2-private vault)."
    },
    "withdrawal_requested_at": {
      "type": "string",
      "format": "date-time",
      "description": "When the subject requested deletion. Triggers a 30-day purge cycle."
    },
    "source_records": {
      "type": "array",
      "description": "MaxRecord IDs that document this DNA test (e.g. screenshots of the test page, signed consent forms, lab reports). Empty array is allowed if test_provider data is the only source.",
      "items": { "type": "string", "format": "uuid" }
    },
    "asserted_by": {
      "type": "string",
      "description": "Agent ID or contributor ID that produced this DNA assertion."
    },
    "asserted_at": {
      "type": "string",
      "format": "date-time"
    },
    "notes": {
      "type": "string",
      "description": "Free-text annotations. Must NEVER contain raw kit numbers, raw genotypes, or unhashed identifiers."
    }
  },
  "additionalProperties": false
}
