diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..f5dab5aa0e27d029777729e7555992cb2b504b69
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,22 @@
+# Auto-generated by the tools/mkowners/mkowners.py tool
+# Uses OWNERS files in different modules throughout the
+# repository as the source of truth for module ownership.
+* @a11r @nicolasnoble @ctiller
+bazel/* @nicolasnoble @a11r @ctiller
+cmake/* @jtattermusch @a11r @nicolasnoble @ctiller
+doc/PROTOCOL-HTTP2.md @ejona86 @a11r @nicolasnoble @ctiller
+doc/interop-test-descriptions.md @ejona86 @a11r @nicolasnoble @ctiller
+etc/* @jboeuf @nicolasnoble @a11r @ctiller
+include/* @ctiller @markdroth @dgquintas @a11r @nicolasnoble
+src/core/* @ctiller @markdroth @dgquintas @a11r @nicolasnoble
+src/cpp/* @ctiller @markdroth @dgquintas @a11r @nicolasnoble
+src/csharp/* @jtattermusch @apolcyn @a11r @nicolasnoble @ctiller
+src/node/* @murgatroid99 @a11r @nicolasnoble @ctiller
+src/objective-c/* @muxi @makdharma @a11r @nicolasnoble @ctiller
+src/php/* @stanley-cheung @murgatroid99 @a11r @nicolasnoble @ctiller
+src/python/* @nathanielmanistaatgoogle @kpayson64 @a11r @nicolasnoble @ctiller
+src/ruby/* @apolcyn @murgatroid99 @a11r @nicolasnoble @ctiller
+tools/* @matt-kwong @jtattermusch @nicolasnoble @a11r @ctiller
+tools/codegen/core/* @ctiller @dgquintas @markdroth
+tools/dockerfile/* @matt-kwong @jtattermusch @nicolasnoble @a11r @ctiller
+tools/run_tests/* @matt-kwong @jtattermusch @nicolasnoble @a11r @ctiller
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..78fc2513b1034e33cc17bfe3e4280793ad266858
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,6 @@
+# Top level ownership
+
+@a11r
+@nicolasnoble
+@ctiller
+
diff --git a/bazel/OWNERS b/bazel/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..40d21e8e0d60b359efcf9ed74795914253b58a5d
--- /dev/null
+++ b/bazel/OWNERS
@@ -0,0 +1,2 @@
+@nicolasnoble
+
diff --git a/cmake/OWNERS b/cmake/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..c0ae0533b350c4949ad511606b817623223dc686
--- /dev/null
+++ b/cmake/OWNERS
@@ -0,0 +1,2 @@
+@jtattermusch
+
diff --git a/doc/OWNERS b/doc/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..f05861799e28df63c386b97a670bfabc71062bb8
--- /dev/null
+++ b/doc/OWNERS
@@ -0,0 +1,2 @@
+@ejona86 PROTOCOL-HTTP2.md interop-test-descriptions.md
+
diff --git a/etc/OWNERS b/etc/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..2dfd7cd61e62619b44607b5833ed8236d6a6d5a2
--- /dev/null
+++ b/etc/OWNERS
@@ -0,0 +1,2 @@
+@jboeuf
+@nicolasnoble
diff --git a/include/OWNERS b/include/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..8dca75ce91618e3c236d726b994062f17948ca7f
--- /dev/null
+++ b/include/OWNERS
@@ -0,0 +1,4 @@
+@ctiller
+@markdroth
+@dgquintas
+
diff --git a/src/core/OWNERS b/src/core/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..8dca75ce91618e3c236d726b994062f17948ca7f
--- /dev/null
+++ b/src/core/OWNERS
@@ -0,0 +1,4 @@
+@ctiller
+@markdroth
+@dgquintas
+
diff --git a/src/cpp/OWNERS b/src/cpp/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..8dca75ce91618e3c236d726b994062f17948ca7f
--- /dev/null
+++ b/src/cpp/OWNERS
@@ -0,0 +1,4 @@
+@ctiller
+@markdroth
+@dgquintas
+
diff --git a/src/csharp/OWNERS b/src/csharp/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..bde3ed985c8a83b3c2f309a610620fbef1ab0c32
--- /dev/null
+++ b/src/csharp/OWNERS
@@ -0,0 +1,3 @@
+@jtattermusch
+@apolcyn
+
diff --git a/src/node/OWNERS b/src/node/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..3a49353d84543e3aebb2adff330f04428d0c0b68
--- /dev/null
+++ b/src/node/OWNERS
@@ -0,0 +1,2 @@
+@murgatroid99
+
diff --git a/src/objective-c/OWNERS b/src/objective-c/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..2bc91186e757736c11428d301c37549e0b39c8d8
--- /dev/null
+++ b/src/objective-c/OWNERS
@@ -0,0 +1,3 @@
+@muxi
+@makdharma
+
diff --git a/src/php/OWNERS b/src/php/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..7065b41132b1ad2ccb083d4aaa0dd30992225bda
--- /dev/null
+++ b/src/php/OWNERS
@@ -0,0 +1,3 @@
+@stanley-cheung
+@murgatroid99
+
diff --git a/src/python/OWNERS b/src/python/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..73a707c67d35b18b22856dc65cf8dc39a2c9e1fb
--- /dev/null
+++ b/src/python/OWNERS
@@ -0,0 +1,3 @@
+@nathanielmanistaatgoogle
+@kpayson64
+
diff --git a/src/ruby/OWNERS b/src/ruby/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..b01fd8061e98409ff5f8e47ad5e3c2787f45c9f3
--- /dev/null
+++ b/src/ruby/OWNERS
@@ -0,0 +1,3 @@
+@apolcyn
+@murgatroid99
+
diff --git a/tools/OWNERS b/tools/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..8f6de536742cec9dbae007b04514d80ce138c8f7
--- /dev/null
+++ b/tools/OWNERS
@@ -0,0 +1,4 @@
+@matt-kwong
+@jtattermusch
+@nicolasnoble
+
diff --git a/tools/codegen/core/OWNERS b/tools/codegen/core/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..17e66479b6e4284fd3e969d1cfd7dfac40ec7bf2
--- /dev/null
+++ b/tools/codegen/core/OWNERS
@@ -0,0 +1,5 @@
+set noparent
+@ctiller
+@dgquintas
+@markdroth
+
diff --git a/tools/dockerfile/OWNERS b/tools/dockerfile/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..20ee8085c212a1984ef8840a2a9d50c5440f403b
--- /dev/null
+++ b/tools/dockerfile/OWNERS
@@ -0,0 +1,3 @@
+@matt-kwong
+@jtattermusch
+
diff --git a/tools/mkowners/mkowners.py b/tools/mkowners/mkowners.py
new file mode 100755
index 0000000000000000000000000000000000000000..e24b052d6715031b6489b6e19d36790ae7f6c38a
--- /dev/null
+++ b/tools/mkowners/mkowners.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python3
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+import collections
+import operator
+import os
+import re
+import subprocess
+
+#
+# Find the root of the git tree
+#
+
+git_root = (subprocess
+            .check_output(['git', 'rev-parse', '--show-toplevel'])
+            .decode('utf-8')
+            .strip())
+
+#
+# Parse command line arguments
+#
+
+default_out = os.path.join(git_root, '.github', 'CODEOWNERS')
+
+argp = argparse.ArgumentParser('Generate .github/CODEOWNERS file')
+argp.add_argument('--out', '-o',
+                  type=str,
+                  default=default_out,
+                  help='Output file (default %s)' % default_out)
+args = argp.parse_args()
+
+#
+# Walk git tree to locate all OWNERS files
+#
+
+owners_files = [os.path.join(root, 'OWNERS')
+                for root, dirs, files in os.walk(git_root)
+                if 'OWNERS' in files]
+
+#
+# Parse owners files
+#
+
+Owners = collections.namedtuple('Owners', 'parent directives dir')
+Directive = collections.namedtuple('Directive', 'who globs')
+
+def parse_owners(filename):
+  with open(filename) as f:
+    src = f.read().splitlines()
+  parent = True
+  directives = []
+  for line in src:
+    line = line.strip()
+    # line := directive | comment
+    if not line: continue
+    if line[0] == '#': continue
+    # it's a directive
+    directive = None
+    if line == 'set noparent':
+      parent = False
+    elif line == '*':
+      directive = Directive(who='*', globs=[])
+    elif ' ' in line:
+      (who, globs) = line.split(' ', 1)
+      globs_list = [glob
+                    for glob in globs.split(' ')
+                    if glob]
+      directive = Directive(who=who, globs=globs_list)
+    else:
+      directive = Directive(who=line, globs=[])
+    if directive:
+      directives.append(directive)
+  return Owners(parent=parent,
+                directives=directives,
+                dir=os.path.relpath(os.path.dirname(filename), git_root))
+
+owners_data = sorted([parse_owners(filename)
+                      for filename in owners_files],
+                     key=operator.attrgetter('dir'))
+
+#
+# Modify owners so that parented OWNERS files point to the actual
+# Owners tuple with their parent field
+#
+
+new_owners_data = []
+for owners in owners_data:
+  if owners.parent == True:
+    best_parent = None
+    best_parent_score = None
+    for possible_parent in owners_data:
+      if possible_parent is owners: continue
+      rel = os.path.relpath(owners.dir, possible_parent.dir)
+      # '..' ==> we had to walk up from possible_parent to get to owners
+      #      ==> not a parent
+      if '..' in rel: continue
+      depth = len(rel.split(os.sep))
+      if not best_parent or depth < best_parent_score:
+        best_parent = possible_parent
+        best_parent_score = depth
+    if best_parent:
+      owners = owners._replace(parent = best_parent.dir)
+    else:
+      owners = owners._replace(parent = None)
+  new_owners_data.append(owners)
+owners_data = new_owners_data
+
+#
+# In bottom to top order, process owners data structures to build up
+# a CODEOWNERS file for GitHub
+#
+
+def glob_intersect(g1, g2):
+  if not g2:
+    return all(c == '*' for c in g1)
+  if not g1:
+    return all(c == '*' for c in g2)
+  c1, *t1 = g1
+  c2, *t2 = g2
+  if c1 == '*':
+    return glob_intersect(g1, t2) or glob_intersect(t1, g2)
+  if c2 == '*':
+    return glob_intersect(t1, g2) or glob_intersect(g1, t2)
+  return c1 == c2 and glob_intersect(t1, t2)
+
+def add_parent_to_globs(parent, globs):
+  if not parent: return
+  for owners in owners_data:
+    if owners.dir == parent:
+      for directive in owners.directives:
+        for dglob in directive.globs or ['*']:
+          for gglob, glob in globs.items():
+            if glob_intersect(dglob, gglob):
+              if directive.who not in glob:
+                glob.append(directive.who)
+      add_parent_to_globs(owners.parent, globs)
+      return
+  assert(False)
+
+todo = owners_data.copy()
+done = set()
+with open(args.out, 'w') as out:
+  out.write('# Auto-generated by the tools/mkowners/mkowners.py tool\n')
+  out.write('# Uses OWNERS files in different modules throughout the\n')
+  out.write('# repository as the source of truth for module ownership.\n')
+  while todo:
+    head, *todo = todo
+    if head.parent and not head.parent in done:
+      todo.append(head)
+      continue
+    globs = collections.OrderedDict()
+    for directive in head.directives:
+      for glob in directive.globs or ['*']:
+        if glob not in globs:
+          globs[glob] = []
+        globs[glob].append(directive.who)
+    add_parent_to_globs(head.parent, globs)
+    for glob, owners in globs.items():
+      out.write('%s %s\n' % (
+          os.path.join(head.dir, glob) if head.dir != '.' else glob,
+          ' '.join(owners)))
+    done.add(head.dir)
diff --git a/tools/run_tests/OWNERS b/tools/run_tests/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..8f6de536742cec9dbae007b04514d80ce138c8f7
--- /dev/null
+++ b/tools/run_tests/OWNERS
@@ -0,0 +1,4 @@
+@matt-kwong
+@jtattermusch
+@nicolasnoble
+
diff --git a/tools/run_tests/sanity/check_owners.sh b/tools/run_tests/sanity/check_owners.sh
new file mode 100755
index 0000000000000000000000000000000000000000..365a278b71bc761da661f99a5cefcd5997270e29
--- /dev/null
+++ b/tools/run_tests/sanity/check_owners.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+set -e
+
+export TEST=true
+
+cd `dirname $0`/../../..
+
+owners=.github/CODEOWNERS
+want_owners=`mktemp /tmp/submXXXXXX`
+
+tools/mkowners/mkowners.py -o $want_owners
+diff -u $owners $want_owners
+
+rm $want_owners
diff --git a/tools/run_tests/sanity/sanity_tests.yaml b/tools/run_tests/sanity/sanity_tests.yaml
index 445f53e55588ae431bf37885d823d82fc3cc3e3a..65cb2fe6a739d0a36cdb39bdc511adbecac65dfc 100644
--- a/tools/run_tests/sanity/sanity_tests.yaml
+++ b/tools/run_tests/sanity/sanity_tests.yaml
@@ -1,5 +1,6 @@
 # a set of tests that are run in parallel for sanity tests
 - script: tools/run_tests/sanity/check_cache_mk.sh
+- script: tools/run_tests/sanity/check_owners.sh
 - script: tools/run_tests/sanity/check_sources_and_headers.py
 - script: tools/run_tests/sanity/check_submodules.sh
 - script: tools/run_tests/sanity/check_test_filtering.py