Project 03 · Integration · Data Control

One Source of Truth Across Three Clouds

Many teams keep the same information in three places — Microsoft 365, Google Workspace, and Notion — and the three never quite match. I built a Python tool that keeps them in sync as one trusted source of truth: it decides who can see what, keeps each file in its proper place, and records every change so it can be checked later.

Python Microsoft 365 Google Workspace Notion Access Control Audit Log
My role
Designed & built the tool
Main record
Notion
Connected
M365 · Google · Notion
Sync
No duplicates, ever
Control
Who sees what + where data lives
Record
Every change is logged

01 The problem: the truth is in three places

Most teams do not have too little information. They have the same information spread across too many apps. The real version of a record is in one place, its files are in another, and the work around it happens in a third. Nobody can answer one simple question: "Where is the correct version, and who is allowed to see it?"

The obvious fix — copy everything into one app — is the wrong move when the data is private or sensitive. Every copy is one more place that can leak. And some files are not allowed to move: rules may say they must stay in a certain account, region, or service. So the challenge was to give people the feeling of one single source of truth, while letting each piece of data stay exactly where the rules say it must.

The key rule

Files are linked, never copied or moved. The main record keeps a tidy row of information plus a link to the real file in its home app. So there is only one copy of any sensitive file, in its proper place, and the tool only adds links and notes — it never makes extra copies.

02 How it is built: one hub, many connectors

The design is simple. Notion is the hub — the main, easy-to-read record that people look at. Each other app (Microsoft 365, Google Workspace) has its own small connector. A connector does four things: log in, get what changed, put it into one shared format, and note where the data is allowed to live. A central part then keeps every app in step with the hub on a schedule, and a control layer wraps the whole thing.

1

Log in

Each app connects with its own secure login and the smallest access it needs.

2

Get changes

It pulls only what changed since last time, so it stays fast.

3

Tidy up

It puts every app's data into one shared format and tags where it must live.

4

Match

It updates the hub by a fixed ID, and links files instead of copying them.

5

Log

It writes down every change so it can be checked later.

connector.py
# Every app follows the same simple shape. Adding a new app
# means writing one small class — nothing else has to change.
class Connector:
    name      = ""
    lives_in  = ""                          # where this data is allowed to live

    def log_in(self): ...
    def get_changes(self, since): ...        # only what changed
    def to_common_format(self, item): ...
    def link_for(self, item): ...           # a link — we never copy the file

03 One record, never a duplicate

The biggest risk in a sync tool is the duplicate. Run it twice and you get two of everything. I stop this by giving every record a fixed ID built from its own details. Then syncing is simple: if that ID is already in the hub, update it; if not, add it. Running the whole tool again always ends in one correct result — no copies.

identity.py
import hashlib

def fixed_id(app, kind, original_id):
    # Same details always give the same ID. So records never duplicate.
    text = f"{app}|{kind}|{original_id}".lower().strip()
    return "FQ-" + hashlib.sha1(text.encode()).hexdigest()[:16]

def sync_one(record, hub):
    key = fixed_id(record.app, record.kind, record.original_id)
    if hub.has(key):
        hub.update(key, record.changed_fields())   # keep human notes safe
    else:
        hub.add(key, record.fields())
It respects people's own work

The tool only updates the fields it owns — values, sync dates, and links. It never overwrites notes that a person wrote by hand. So the automatic updates and the human notes live side by side without fighting.

04 Control: who can see it, and where it lives

Bringing data together is only safe if you also bring the rules together. The control layer adds two checks to every record:

ControlWhat it doesHow
Who can see whatSets read/write rights per type of recordA role-to-permission list is checked before any read or write
Where data livesKeeps each record and file in its allowed placeEach record carries a fixed "lives in" tag; moves out of bounds are refused
Which app winsDecides the main app when two disagreeA main/backup order, so a backup can add to but not replace the main
Least accessLimits what each connector can touchEach connector only gets access to the exact data it needs

Because files are linked and not copied, a file that must stay in one place physically never leaves it. The hub just holds a link, and the rules decide who may open it. This is the same careful, "keep data where it belongs" approach that strict environments need — and it is exactly what data-protection laws like GDPR and KVKK are asking for.

05 Checking the work and keeping a safe record

A tool that controls data must be able to show what it did. After each sync, it counts the records and checks the totals before calling the run a success. And it writes every action to a safe, add-only log where each entry includes a code from the entry before it. If anyone tries to change the history later, the codes stop matching and the change is easy to spot.

log.py
import hashlib, json

def add_to_log(log, action):
    last = log[-1]["code"] if log else "START"
    text = json.dumps(action, sort_keys=True)
    code = hashlib.sha256(f"{last}{text}".encode()).hexdigest()
    log.append({"action": action, "after": last, "code": code})
    return log   # change any old entry and every later code breaks
  • Count and total checks after each run — the run only passes if the numbers add up.
  • A safe history that proves the record of changes has not been edited later.
  • A preview mode that shows every add, update, and link before anything touches a live system — easy to review and safe to demo.

06 Why this matters

The same design that brings a company's apps together is also the design that careful, regulated work needs: one clear main record, connectors that respect each app's limits, fixed IDs so nothing leaks or duplicates, and a record you can check. It is the everyday-business version of the strict separation work I did for US government projects — and it shows, in real Python code, the "keep data where it belongs and control who sees it" approach that GDPR and KVKK are built around.

Want your apps to agree on one truth?

I build sync tools that feel like one system without breaking the rules.

[email protected]