diff --git a/colleges/migrations/0015_auto_20200906_0714.py b/colleges/migrations/0015_auto_20200906_0714.py
new file mode 100644
index 0000000000000000000000000000000000000000..fa513656155d7624392548d52cbd40147735daad
--- /dev/null
+++ b/colleges/migrations/0015_auto_20200906_0714.py
@@ -0,0 +1,35 @@
+# Generated by Django 2.2.11 on 2020-09-06 05:14
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ontology', '0007_Branch_Field_Specialty'),
+        ('colleges', '0014_auto_20190419_1150'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='College',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(help_text='Official name of the College (default: name of the discipline)', max_length=256, unique=True)),
+                ('order', models.PositiveSmallIntegerField()),
+                ('acad_field', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='colleges', to='ontology.AcademicField')),
+            ],
+            options={
+                'ordering': ['acad_field', 'order'],
+            },
+        ),
+        migrations.AddConstraint(
+            model_name='college',
+            constraint=models.UniqueConstraint(fields=('name', 'acad_field'), name='college_unique_name_acad_field'),
+        ),
+        migrations.AddConstraint(
+            model_name='college',
+            constraint=models.UniqueConstraint(fields=('acad_field', 'order'), name='college_unique_acad_field_order'),
+        ),
+    ]
diff --git a/colleges/models/__init__.py b/colleges/models/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e7aeb5facfaeb700d6dfb74b11fab5240d3a597
--- /dev/null
+++ b/colleges/models/__init__.py
@@ -0,0 +1,9 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from .college import College
+
+from .fellowship import Fellowship
+
+from .potential_fellowship import PotentialFellowship, PotentialFellowshipEvent
diff --git a/colleges/models/college.py b/colleges/models/college.py
new file mode 100644
index 0000000000000000000000000000000000000000..51a64d7222a06872b7207b069951dc6569e3d30a
--- /dev/null
+++ b/colleges/models/college.py
@@ -0,0 +1,64 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.db import models
+
+from scipost.constants import SCIPOST_DISCIPLINES
+
+from ontology.models import Specialty
+
+
+class College(models.Model):
+    """
+    Anchor for a set of Fellows handling a set of Journals.
+
+    A College has a ForeignKey to AcademicField.
+
+    Specialties are defined as a `@property` and extracted via the Journals
+    which are ForeignKey-related back to College.
+
+    The `@property` `is_field_wide` checks the Journals run by the College and
+    returns a Boolean specifying whether the College operates field-wide, or is specialized.
+    """
+
+    name = models.CharField(
+        max_length=256,
+        help_text='Official name of the College (default: name of the discipline)',
+        unique=True
+    )
+
+    acad_field = models.ForeignKey(
+        'ontology.AcademicField',
+        on_delete=models.PROTECT,
+        related_name='colleges'
+    )
+
+    order = models.PositiveSmallIntegerField()
+
+    class Meta:
+        constraints = [
+            models.UniqueConstraint(
+                fields=['name', 'acad_field',],
+                name='college_unique_name_acad_field'
+            ),
+            models.UniqueConstraint(
+                fields=['acad_field', 'order'],
+                name='college_unique_acad_field_order'
+            ),
+        ]
+        ordering = [
+            'acad_field',
+            'order'
+        ]
+
+    def __str__(self):
+        return "Editorial College (%s)" % self.name
+
+    @property
+    def specialties(self):
+        return Specialty.objects.filter(journals__college__pk=self.id)
+
+    @property
+    def is_field_wide(self):
+        return len(self.specialties) == 0
diff --git a/colleges/models/fellowship.py b/colleges/models/fellowship.py
new file mode 100644
index 0000000000000000000000000000000000000000..9553161ce886fbc2333b12ac71dc1b73bbc977de
--- /dev/null
+++ b/colleges/models/fellowship.py
@@ -0,0 +1,62 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+import datetime
+
+from django.db import models
+from django.urls import reverse
+
+from ..managers import FellowQuerySet
+
+from scipost.behaviors import TimeStampedModel
+from scipost.models import get_sentinel_user
+
+
+class Fellowship(TimeStampedModel):
+    """A Fellowship gives access to the Submission Pool to Contributors.
+
+    Editorial College Fellowship connects the Editorial College and Contributors,
+    possibly with a limiting start/until date and/or a Proceedings event.
+
+    The date range will effectively be used while determining 'the pool' for a specific
+    Submission, so it has a direct effect on the submission date.
+    """
+
+    contributor = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE,
+                                    related_name='fellowships')
+    start_date = models.DateField(null=True, blank=True)
+    until_date = models.DateField(null=True, blank=True)
+
+    guest = models.BooleanField('Guest Fellowship', default=False)
+
+    objects = FellowQuerySet.as_manager()
+
+    class Meta:
+        ordering = ['contributor__user__last_name']
+        unique_together = ('contributor', 'start_date', 'until_date')
+
+    def __str__(self):
+        _str = self.contributor.__str__()
+        if self.guest:
+            _str += ' (guest fellowship)'
+        return _str
+
+    def get_absolute_url(self):
+        """Return the admin fellowship page."""
+        return reverse('colleges:fellowship_detail', kwargs={'pk': self.id})
+
+    def sibling_fellowships(self):
+        """Return all Fellowships that are directly related to the Fellow of this Fellowship."""
+        return self.contributor.fellowships.all()
+
+    def is_active(self):
+        """Check if the instance is within start and until date."""
+        today = datetime.date.today()
+        if not self.start_date:
+            if not self.until_date:
+                return True
+            return today <= self.until_date
+        elif not self.until_date:
+            return today >= self.start_date
+        return today >= self.start_date and today <= self.until_date
diff --git a/colleges/models/potential_fellowship.py b/colleges/models/potential_fellowship.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c829c17e01a234c22743d7f2ee9ca99b72420d1
--- /dev/null
+++ b/colleges/models/potential_fellowship.py
@@ -0,0 +1,77 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+import datetime
+
+from django.db import models
+from django.utils import timezone
+
+from scipost.models import get_sentinel_user
+
+from ..constants import POTENTIAL_FELLOWSHIP_STATUSES,\
+    POTENTIAL_FELLOWSHIP_IDENTIFIED, POTENTIAL_FELLOWSHIP_EVENTS
+from ..managers import PotentialFellowshipQuerySet
+
+
+
+class PotentialFellowship(models.Model):
+    """
+    A PotentialFellowship is defined when a researcher has been identified by
+    Admin or EdAdmin as a potential member of an Editorial College,
+    or when a current Advisory Board member or Fellow nominates the person.
+
+    It is linked to Profile as ForeignKey and not as OneToOne, since the same
+    person can eventually be approached on different occasions.
+
+    Using Profile allows to consider both registered Contributors
+    and non-registered people.
+    """
+
+    profile = models.ForeignKey('profiles.Profile', on_delete=models.CASCADE)
+    status = models.CharField(max_length=32, choices=POTENTIAL_FELLOWSHIP_STATUSES,
+                              default=POTENTIAL_FELLOWSHIP_IDENTIFIED)
+    in_agreement = models.ManyToManyField(
+        'scipost.Contributor',
+        related_name='in_agreement_with_election', blank=True)
+    in_abstain = models.ManyToManyField(
+        'scipost.Contributor',
+        related_name='in_abstain_with_election', blank=True)
+    in_disagreement = models.ManyToManyField(
+        'scipost.Contributor',
+        related_name='in_disagreement_with_election', blank=True)
+    voting_deadline = models.DateTimeField('voting deadline', default=timezone.now)
+    elected = models.NullBooleanField()
+
+    objects = PotentialFellowshipQuerySet.as_manager()
+
+    class Meta:
+        ordering = ['profile__last_name']
+
+    def __str__(self):
+        return '%s, %s' % (self.profile.__str__(), self.get_status_display())
+
+    def latest_event_details(self):
+        event = self.potentialfellowshipevent_set.order_by('-noted_on').first()
+        if not event:
+            return 'No event recorded'
+        return '%s [%s]' % (event.get_event_display(), event.noted_on.strftime('%Y-%m-%d'))
+
+
+class PotentialFellowshipEvent(models.Model):
+    """Any event directly related to a PotentialFellowship instance registered as plain text."""
+
+    potfel = models.ForeignKey('colleges.PotentialFellowship', on_delete=models.CASCADE)
+    event = models.CharField(max_length=32, choices=POTENTIAL_FELLOWSHIP_EVENTS)
+    comments = models.TextField(blank=True)
+
+    noted_on = models.DateTimeField(auto_now_add=True)
+    noted_by = models.ForeignKey('scipost.Contributor',
+                                 on_delete=models.SET(get_sentinel_user),
+                                 blank=True, null=True)
+
+    def __str__(self):
+        return '%s, %s %s: %s' % (self.potfel.profile.last_name,
+                                  self.potfel.profile.get_title_display(),
+                                  self.potfel.profile.first_name,
+                                  self.get_event_display())